49 Commits
Author SHA1 Message Date
Syaharani 53913bebe9 update kelompok tani 2026-11-03 17:53:53 +07:00
Althaf f455f8b878 Merge branch 'main' of https://git.probindo.com/Probindo/database-pertani-web 2026-11-03 14:47:04 +07:00
Althaf f1112e3b68 update controller 2026-11-03 10:51:50 +07:00
afriadirahman c1370a26ca Update Harga Internasional 2026-11-03 09:37:59 +07:00
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
113 changed files with 21743 additions and 4787 deletions
+6
View File
@@ -63,3 +63,9 @@ AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
VITE_APP_NAME="${APP_NAME}"
# Optional CA bundle path for Yahoo Finance when PHP has no system CA configured.
YAHOO_FINANCE_CA_BUNDLE=
ALPHA_VANTAGE_API_KEY=
+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';
}
@@ -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)',
};
}
@@ -5,6 +5,7 @@ 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;
@@ -22,10 +23,7 @@ class PengurusBumdesController extends Controller
}
$query = PengurusBumdes::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',
...$this->wilayahRelationsWithoutProgramScope(),
'creator:id,name',
])
->orderBy('created_at', 'desc');
@@ -70,7 +68,7 @@ class PengurusBumdesController extends Controller
abort(403);
}
$provinsi = Provinsi::select('id', 'kode', 'nama')->orderBy('kode', 'asc')->get();
$provinsi = $this->provinsiOptions();
$jabatan = JabatanBumdes::options();
$pendidikan = Pendidikan::options();
@@ -106,10 +104,10 @@ class PengurusBumdesController extends Controller
abort(403);
}
$pengurus = $this->pengurusQuery()->with([
'desaKelurahan.kecamatan.kabupatenKota.provinsi',
])->findOrFail($id);
$provinsi = Provinsi::select('id', 'kode', 'nama')->orderBy('kode', 'asc')->get();
$pengurus = $this->pengurusQuery()
->with($this->wilayahRelationsWithoutProgramScope())
->findOrFail($id);
$provinsi = $this->provinsiOptions();
$jabatan = JabatanBumdes::options();
$pendidikan = Pendidikan::options();
@@ -152,11 +150,41 @@ class PengurusBumdesController extends Controller
abort(403);
}
$pengurus = $this->pengurusQuery()->with([
'desaKelurahan.kecamatan.kabupatenKota.provinsi',
])->findOrFail($id);
$pengurus = $this->pengurusQuery()
->with([
...$this->wilayahRelationsWithoutProgramScope(),
'kelembagaanAktor',
])
->findOrFail($id);
$kelembagaanAktor = $pengurus->kelembagaanAktor;
return view('bumdes.kelembagaan-aktor.index', compact('pengurus'));
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()
@@ -170,6 +198,24 @@ class PengurusBumdesController extends Controller
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([
@@ -215,4 +261,320 @@ class PengurusBumdesController extends Controller
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',
];
}
}
@@ -11,6 +11,7 @@ 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
{
@@ -20,7 +21,56 @@ class DemografiPetaniController extends Controller
abort(403);
}
$petaniCounts = Petani::selectRaw("
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
@@ -30,14 +80,18 @@ class DemografiPetaniController extends Controller
$petaniLakilaki = $petaniCounts->laki;
$petaniPerempuan = $petaniCounts->perempuan;
$totalKomoditas = Petani::whereNotNull('komoditas_id')
->distinct()
->count('komoditas_id');
$totalKomoditas = Komoditas::query()
->where('is_utama', true)
->whereHas('petani', fn ($query) => $query->whereIn('petani.id', clone $filteredPetaniIds))
->count();
$totalLuasLahan = Lahan::has('petani')->sum('luas_lahan');
$totalLuasLahan = Lahan::has('petani')
->whereIn('petani_id', clone $filteredPetaniIds)
->sum('luas_lahan');
$ratarataLuasLahan = $totalLuasLahan && $totalPetani ? $totalLuasLahan / $totalPetani : 0;
$umurCounts = Petani::whereNotNull('tanggal_lahir')->selectRaw("
$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,
@@ -45,7 +99,8 @@ class DemografiPetaniController extends Controller
COUNT(*) FILTER (WHERE EXTRACT(YEAR FROM AGE(CURRENT_DATE, tanggal_lahir)) >= 60) as umur_60_keatas
")->first();
$pendidikanCounts = Petani::whereNotNull('pendidikan_terakhir')
$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');
@@ -59,20 +114,71 @@ class DemografiPetaniController extends Controller
'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')
->withCount('petani')
->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()->select('petani_id')->selectRaw('SUM(luas_lahan) as total_luas')->groupBy('petani_id'),
Lahan::query()
->whereIn('petani_id', clone $filteredPetaniIds)
->select('petani_id')
->selectRaw('luas_lahan'),
'agg'
)->selectRaw("
COUNT(*) FILTER (WHERE total_luas < 5000) as kurang_05,
COUNT(*) FILTER (WHERE total_luas >= 5000 AND total_luas <= 10000) as luas_05_1,
COUNT(*) FILTER (WHERE total_luas > 10000 AND total_luas <= 20000) as luas_1_2,
COUNT(*) FILTER (WHERE total_luas > 20000) as lebih_2
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 = [
@@ -82,14 +188,27 @@ class DemografiPetaniController extends Controller
'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(
PendapatanKomoditas::query()
->join('sosial_ekonomi', 'sosial_ekonomi.id', '=', 'pendapatan_komoditas.sosial_ekonomi_id')
->whereNotNull('sosial_ekonomi.petani_id')
->groupBy('sosial_ekonomi.petani_id')
->selectRaw('SUM(pendapatan_komoditas.pendapatan) as total_pendapatan'),
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,
@@ -97,6 +216,8 @@ class DemografiPetaniController extends Controller
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,
@@ -112,6 +233,7 @@ class DemografiPetaniController extends Controller
'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,
@@ -120,31 +242,88 @@ class DemografiPetaniController extends Controller
'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,
];
$daftarPetani = Petani::with(['komoditas'])
->withSum('lahans as total_luas_lahan', 'luas_lahan')
->orderBy('id')
->paginate(10);
return compact('data');
}
return view('dashboard.demografi-petani', compact('data', 'daftarPetani'));
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]);
@@ -187,6 +366,45 @@ class DemografiPetaniController extends Controller
}
}
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);
}
@@ -213,15 +431,28 @@ class DemografiPetaniController extends Controller
}
if ($request->filled('luas_lahan')) {
$lahanQuery = Lahan::query()->select('petani_id')->groupBy('petani_id');
$lahanQuery = Lahan::query()
->whereIn('petani_id', clone $filteredPetaniIds)
->select('petani_id')
->distinct();
match ($request->luas_lahan) {
'kurang_05' => $lahanQuery->havingRaw('SUM(luas_lahan) < 5000'),
'05_1' => $lahanQuery->havingRaw('SUM(luas_lahan) >= 5000 AND SUM(luas_lahan) <= 10000'),
'1_2' => $lahanQuery->havingRaw('SUM(luas_lahan) > 10000 AND SUM(luas_lahan) <= 20000'),
'lebih_2' => $lahanQuery->havingRaw('SUM(luas_lahan) > 20000'),
'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('id', $lahanQuery->pluck('petani_id'));
$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);
@@ -5,21 +5,14 @@ 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 Illuminate\Support\Facades\Auth;
use Illuminate\Http\RedirectResponse;
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\Tanaman;
use App\Models\Pertanian\SkorPetani;
use App\Models\Wilayah\Provinsi;
use App\Models\Wilayah\KabupatenKota;
use App\Models\Wilayah\Kecamatan;
use App\Models\Wilayah\DesaKelurahan;
class IndeksPetaniController extends Controller
{
@@ -36,13 +29,13 @@ class IndeksPetaniController extends Controller
];
}
private function petaniQuery(array $filters): \Illuminate\Database\Eloquent\Builder
private function petaniQuery(array $filters): Builder
{
$petaniQuery = Petani::query()
->join('master_desa_kelurahan', 'petani.desa_kelurahan_id', '=', 'master_desa_kelurahan.id')
->join('master_kecamatan', 'master_desa_kelurahan.kecamatan_id', '=', 'master_kecamatan.id')
->join('master_kabupaten_kota', 'master_kecamatan.kabupaten_kota_id', '=', 'master_kabupaten_kota.id')
->join('master_provinsi', 'master_kabupaten_kota.provinsi_id', '=', 'master_provinsi.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))
@@ -50,17 +43,21 @@ class IndeksPetaniController extends Controller
->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): \Illuminate\Database\Eloquent\Builder
private function skorPetaniQuery(array $filters, array $komoditas): Builder
{
$skor = SkorPetani::query()
->join('petani', 'petani_skor.petani_id', '=', 'petani.id')
->join('master_desa_kelurahan', 'petani.desa_kelurahan_id', '=', 'master_desa_kelurahan.id')
->join('master_kecamatan', 'master_desa_kelurahan.kecamatan_id', '=', 'master_kecamatan.id')
->join('master_kabupaten_kota', 'master_kecamatan.kabupaten_kota_id', '=', 'master_kabupaten_kota.id')
->join('master_provinsi', 'master_kabupaten_kota.provinsi_id', '=', 'master_provinsi.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))
@@ -68,33 +65,31 @@ class IndeksPetaniController extends Controller
->when($filters['desaKelurahan_id'], fn($q, $id) => $q->where('petani.desa_kelurahan_id', $id))
->whereIn('petani.komoditas_id', $komoditas);
if ($filters['segmen']) {
$skor->where(function ($q) use ($filters) {
match ($filters['segmen']) {
'Penggerak' => $q->where('skor_kesiapan_adopsi', '>=', 4),
'Perintis' => $q->where(function($q) {
$q->where('skor_kesiapan_adopsi', '>=', 3)
->where('skor_kesiapan_adopsi', '<', 4);
}),
'Pemerhati' => $q->where(function($q) {
$q->where('skor_kesiapan_adopsi', '>=', 2)
->where('skor_kesiapan_adopsi', '<', 3);
}),
'Pemula' => $q->where(function($q) {
$q->where('skor_kesiapan_adopsi', '>=', 1)
->where('skor_kesiapan_adopsi', '<', 2);
}),
'Belum Lengkap' => $q->where(function ($q) {
$q->whereNull('skor_kesiapan_adopsi')
->orWhere('skor_kesiapan_adopsi', '<', 1);
}),
return $this->applySegmentFilter($skor, $filters['segmen']);
}
default => null,
};
});
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));
}
return $skor;
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)
@@ -103,18 +98,28 @@ class IndeksPetaniController extends Controller
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);
$title = 'Dashboard Profil Petani';
$segments = $this->segments();
$colors = collect($segments)
->pluck('color')
->map(fn($c) => "var(--bs-{$c})")
->values();
$komoditas = Komoditas::where('is_utama', true)->get();
$petaniQuery = $this->petaniQuery($filters);
$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_desa_kelurahan.path as path,
master_kecamatan.id as kecamatan_id,
master_kecamatan.nama as kecamatan,
master_kabupaten_kota.id as kabupaten_id,
@@ -122,9 +127,15 @@ class IndeksPetaniController extends Controller
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) {
@@ -147,7 +158,6 @@ class IndeksPetaniController extends Controller
return [
'id' => $desa->desa_kelurahan_id,
'nama' => $desa->desa_kelurahan,
'path' => $desa->path,
];
})->values(),
];
@@ -157,12 +167,15 @@ class IndeksPetaniController extends Controller
];
})->values();
return view('dashboard.indeks-petani', array_merge($filters,
return array_merge($filters,
compact(
'komoditas',
'desaKelurahan',
'provinsi',
'wilayah',
)));
'segments',
'colors',
));
}
public function summary(Request $request): JsonResponse
@@ -171,23 +184,21 @@ class IndeksPetaniController extends Controller
$petaniQuery = $this->petaniQuery($filters);
$komoditas = Komoditas::where('is_utama', true)
->when($filters['komoditas_id'], fn($q, $id) => $q->where('id', $id))->get();
->when($filters['komoditas_id'], fn($q, $id) => $q->where('id', $id))->get(['id', 'nama']);
$countKomoditas = $petaniQuery->clone()
->whereHas('komoditas', fn ($query) => $query->where('is_utama', true))
->distinct()->count('komoditas_id');
$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')
->distinct()
->pluck('petani.id');
$sumLahan = Lahan::has('petani')->whereIn('petani_id', $filteredPetaniIds)->whereIn('komoditas_id', $komoditas->pluck('id'))->sum('luas_lahan');
$avgLahan = Lahan::has('petani')->whereIn('petani_id', $filteredPetaniIds)->whereIn('komoditas_id', $komoditas->pluck('id'))->avg('luas_lahan');
$countDesa = $petaniQuery->clone()->whereIn('komoditas_id', $komoditas->pluck('id'))->distinct()->count('desa_kelurahan_id');
$countKecamatan = $petaniQuery->clone()->whereIn('komoditas_id', $komoditas->pluck('id'))->distinct()->count('master_kecamatan.id');
$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');
@@ -205,12 +216,12 @@ class IndeksPetaniController extends Controller
}
return response()->json([
'petani' => $petaniQuery->clone()->count(),
'komoditas' => $countKomoditas,
'sum_lahan' => $sumLahan,
'avg_lahan' => $avgLahan,
'desa' => $countDesa,
'kecamatan' => $countKecamatan,
'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
]);
@@ -269,7 +280,7 @@ class IndeksPetaniController extends Controller
$case = $this->cases();
$komoditas = Komoditas::where('is_utama', true)
->when($filters['komoditas_id'], fn($q, $id) => $q->where('id', $id))->get();
->when($filters['komoditas_id'], fn($q, $id) => $q->where('id', $id))->get(['id', 'nama']);
$skorQuery = $this->skorPetaniQuery($filters, $komoditas->pluck('id')->toArray());
@@ -313,7 +324,7 @@ class IndeksPetaniController extends Controller
$case = $this->cases();
$komoditas = Komoditas::where('is_utama', true)
->when($filters['komoditas_id'], fn($q, $id) => $q->where('id', $id))->get();
->when($filters['komoditas_id'], fn($q, $id) => $q->where('id', $id))->get(['id', 'nama']);
$skorQuery = $this->skorPetaniQuery($filters, $komoditas->pluck('id')->toArray());
@@ -355,7 +366,7 @@ class IndeksPetaniController extends Controller
$filters = $this->filters($request);
$komoditas = Komoditas::where('is_utama', true)
->when($filters['komoditas_id'], fn($q, $id) => $q->where('id', $id))->get();
->when($filters['komoditas_id'], fn($q, $id) => $q->where('id', $id))->get(['id', 'nama']);
$skorQuery = $this->skorPetaniQuery($filters, $komoditas->pluck('id')->toArray());
@@ -380,7 +391,7 @@ class IndeksPetaniController extends Controller
$filters = $this->filters($request);
$komoditas = Komoditas::where('is_utama', true)
->when($filters['komoditas_id'], fn($q, $id) => $q->where('id', $id))->get();
->when($filters['komoditas_id'], fn($q, $id) => $q->where('id', $id))->get(['id', 'nama']);
$skorQuery = $this->skorPetaniQuery($filters, $komoditas->pluck('id')->toArray());
@@ -405,17 +416,8 @@ class IndeksPetaniController extends Controller
]);
}
public function map(Request $request): JsonResponse
private function villageData(array $filters, bool $includePath = true): \Illuminate\Support\Collection
{
$filters = $this->filters($request);
$segments = $this->segments();
$colors = collect($segments)
->pluck('color')
->map(fn($c) => "var(--bs-{$c})")
->values();
$segmentStyles = collect($segments)->keyBy('name');
$case = $this->cases();
$petaniQuery = $this->petaniQuery($filters);
@@ -424,7 +426,6 @@ class IndeksPetaniController extends Controller
->selectRaw("
master_desa_kelurahan.id as desa_kelurahan_id,
master_desa_kelurahan.nama as desa_kelurahan,
master_desa_kelurahan.path as path,
master_kecamatan.id as kecamatan_id,
master_kecamatan.nama as kecamatan,
master_kabupaten_kota.id as kabupaten_id,
@@ -432,11 +433,12 @@ class IndeksPetaniController extends Controller
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();
->when($filters['komoditas_id'], fn($q, $id) => $q->where('id', $id))->get(['id', 'nama']);
$skorQuery = $this->skorPetaniQuery($filters, $komoditas->pluck('id')->toArray());
@@ -464,17 +466,21 @@ class IndeksPetaniController extends Controller
->groupBy('petani.desa_kelurahan_id')
->get();
$segmenDominan = $mapSegmentasi
->groupBy('desa_kelurahan_id')
$segmentasiGroups = $mapSegmentasi->groupBy('desa_kelurahan_id');
$jumlahSegmenByDesa = $segmentasiGroups
->map(fn ($items) => $items->pluck('total', 'segmen'));
$segmenDominan = $segmentasiGroups
->map(function ($items) {
return $items->sortByDesc('total')->first();
return $items->reduce(fn ($largest, $item) => $largest === null || $item->total > $largest->total ? $item : $largest);
});
$mapAvgSkorSegmentasi->each(function ($item) use ($segmenDominan) {
$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');
@@ -490,97 +496,22 @@ class IndeksPetaniController extends Controller
return $desa;
})->filter()->values();
return response()->json($mapDesaSegmentasi);
return $mapDesaSegmentasi;
}
public function map(Request $request): JsonResponse
{
return response()->json($this->villageData($this->filters($request)));
}
public function desa(Request $request): JsonResponse
{
$filters = $this->filters($request);
$segments = $this->segments();
$segmentStyles = collect($this->segments())->keyBy('name');
$mapDesaSegmentasi = $this->villageData($this->filters($request), false);
$colors = collect($segments)
->pluck('color')
->map(fn($c) => "var(--bs-{$c})")
->values();
$segmentStyles = collect($segments)->keyBy('name');
$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_desa_kelurahan.path as path,
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
")
->distinct()
->get();
$komoditas = Komoditas::where('is_utama', true)
->when($filters['komoditas_id'], fn($q, $id) => $q->where('id', $id))->get();
$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();
$segmenDominan = $mapSegmentasi
->groupBy('desa_kelurahan_id')
->map(function ($items) {
return $items->sortByDesc('total')->first();
});
$mapAvgSkorSegmentasi->each(function ($item) use ($segmenDominan) {
$dominan = $segmenDominan->get($item->desa_kelurahan_id);
$item->segmen_dominan = $dominan?->segmen;
$item->jumlah_dominan = $dominan?->total;
});
$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();
$rows = $mapDesaSegmentasi->map(function ($desa) use ($segmentStyles) {
$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'];
@@ -594,6 +525,16 @@ class IndeksPetaniController extends Controller
'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(),
];
});
@@ -605,30 +546,21 @@ class IndeksPetaniController extends Controller
$filters = $this->filters($request);
$segments = $this->segments();
$colors = collect($segments)
->pluck('color')
->map(fn($c) => "var(--bs-{$c})")
->values();
$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();
->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
->filter(function ($item) use ($filters) {
if (!$filters['segmen']) {
return true;
}
$segmen = $item->skor ? $item->segmen() : 'Belum Lengkap';
return $segmen === $filters['segmen'];
})
->map(function ($item) use ($segmentStyles) {
$segmen = $item->skor
? $item->segmen()
@@ -642,7 +574,7 @@ class IndeksPetaniController extends Controller
'desa' => $item->desaKelurahan?->nama,
'desa_kelurahan_id' => $item->desa_kelurahan_id,
'komoditas' => $item->komoditas?->nama,
'luas_lahan' => $item->lahans?->sum('luas_lahan') ?? 0,
'luas_lahan' => (float) ($item->total_luas_lahan ?? 0),
'skor' => $item->skor
? $item->skor->skor_kesiapan_adopsi
: null,
@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Web\Dashboard;
use App\Enums\KeparahanSerangan;
use App\Enums\PenyakitTanaman;
use App\Enums\PraktikBudidaya\KondisiPengelolaanAir;
use App\Enums\PraktikBudidaya\PemberianPupuk;
use App\Enums\SeranganHama;
use App\Enums\SosialEkonomi\StatusKepemilikanLahanSosialEkonomi;
@@ -13,12 +14,14 @@ use App\Models\Pertanian\Lahan;
use App\Models\Pertanian\Petani;
use App\Models\Pertanian\PraktikBudidaya;
use App\Models\Pertanian\Produksi;
use App\Models\Pertanian\SurveyPanen;
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 App\Models\Wilayah\Provinsi;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
@@ -95,11 +98,180 @@ class IndikatorPertanianController extends Controller
private function dashboardPayload(array $filters): array
{
return [
'overviewData' => $this->overviewData($filters),
'landProfileData' => $this->landProfileData($filters),
'summaryData' => $this->summaryData($filters),
'chartData' => $this->chartData($filters),
];
}
private function overviewData(array $filters): array
{
$totalPetani = $this->applyPetaniFilters(Petani::query(), $filters)->count();
$totalLahan = $this->applyLahanFilters(Lahan::query(), $filters)->count();
$totalLuasLahan = (float) $this->applyLahanFilters(Lahan::has('petani'), $filters)->sum('luas_lahan');
$jumlahDesa = $this->applyLahanFilters(Lahan::query(), $filters)
->whereNotNull('desa_kelurahan_id')
->distinct('desa_kelurahan_id')
->count('desa_kelurahan_id');
$jumlahKecamatan = $this->applyLahanFilters(Lahan::query(), $filters)
->leftJoin('master_desa_kelurahan as d', 'd.id', '=', 'map_lahan.desa_kelurahan_id')
->whereNotNull('d.kecamatan_id')
->distinct('d.kecamatan_id')
->count('d.kecamatan_id');
$totalProduksiKg = $this->totalProduksiKg($filters);
$rataRataProduktivitas = $totalLuasLahan > 0 ? $totalProduksiKg / $totalLuasLahan : 0;
$tanamanProduktif = $this->tanamanProduktifCount($filters);
$batchPanen = $this->batchPanenCount($filters);
return [
'kpiCards' => [
[
'value' => number_format($totalPetani, 0, ',', '.'),
'label' => 'Jumlah Petani',
'description' => 'Petani yang tercakup dalam filter aktif',
'icon' => 'tabler-users',
'variant' => 'primary',
],
[
'value' => number_format($totalLahan, 0, ',', '.'),
'label' => 'Jumlah Kebun/Lahan',
'description' => 'Kebun atau lahan terdata',
'icon' => 'tabler-trees',
'variant' => 'success',
],
[
'value' => number_format($totalLuasLahan, 2, ',', '.').' ha',
'label' => 'Luas Lahan',
'description' => 'Akumulasi luas lahan terdata',
'icon' => 'tabler-ruler-measure',
'variant' => 'warning',
],
[
'value' => number_format($jumlahDesa, 0, ',', '.').' desa',
'label' => 'Desa/Kecamatan',
'description' => number_format($jumlahKecamatan, 0, ',', '.').' kecamatan tercakup',
'icon' => 'tabler-map-pin',
'variant' => 'info',
],
[
'value' => number_format($totalProduksiKg, 0, ',', '.').' kg',
'label' => 'Total Produksi',
'description' => $this->productionSummaryDescription($filters),
'icon' => 'tabler-wheat',
'variant' => 'primary',
],
[
'value' => number_format($rataRataProduktivitas, 0, ',', '.').' kg/ha',
'label' => 'Produktivitas Rata-rata',
'description' => 'Berdasarkan produksi dan luas lahan terdata',
'icon' => 'tabler-chart-bar',
'variant' => 'success',
],
[
'value' => number_format($tanamanProduktif, 0, ',', '.'),
'label' => 'Tanaman Produktif',
'description' => 'Tanaman dengan kondisi baik',
'icon' => 'tabler-plant-2',
'variant' => 'warning',
],
[
'value' => number_format($batchPanen, 0, ',', '.'),
'label' => 'Batch Panen Tercatat',
'description' => 'Catatan produksi dan survey panen',
'icon' => 'tabler-basket',
'variant' => 'info',
],
],
'commodityFocus' => $this->commodityFocusData($filters),
'map' => [
'features' => $this->farmMapFeatures($filters),
'levels' => $this->mapLevelData($filters),
],
];
}
private function landProfileData(array $filters): array
{
$totalTanaman = (int) $this->applyLahanFilters(Lahan::query(), $filters)->sum('jumlah_tanaman');
$rataRataUmurTanaman = (float) $this->applyLahanFilters(Lahan::query(), $filters)
->whereNotNull('umur_tanaman')
->avg('umur_tanaman');
$komoditasCount = (int) $this->applyLahanFilters(Lahan::query(), $filters)
->whereNotNull('komoditas_id')
->distinct('komoditas_id')
->count('komoditas_id');
$statusLahanCount = (int) $this->applyLahanFilters(Lahan::query(), $filters)
->whereNotNull('status_kepemilikan')
->distinct('status_kepemilikan')
->count('status_kepemilikan');
$tanamanPenaung = $this->shadowTreeSummary($filters);
$tanamanPendamping = $this->companionPlantSummary($filters);
$diversityAverage = $this->plantDiversityAverage($filters);
return [
'conditionCards' => [
[
'value' => number_format($komoditasCount, 0, ',', '.'),
'label' => 'Luas Lahan per Komoditas',
'description' => 'Komoditas dengan luas lahan terdata',
'icon' => 'tabler-chart-treemap',
'variant' => 'primary',
],
[
'value' => number_format($totalTanaman, 0, ',', '.'),
'label' => 'Jumlah Tanaman',
'description' => 'Akumulasi jumlah tanaman pada lahan',
'icon' => 'tabler-trees',
'variant' => 'success',
],
[
'value' => number_format($rataRataUmurTanaman, 1, ',', '.').' tahun',
'label' => 'Umur Tanaman',
'description' => 'Rata-rata umur tanaman terdata',
'icon' => 'tabler-calendar-stats',
'variant' => 'warning',
],
[
'value' => number_format($statusLahanCount, 0, ',', '.'),
'label' => 'Status Lahan',
'description' => 'Jumlah kategori status kepemilikan lahan',
'icon' => 'tabler-certificate',
'variant' => 'info',
],
],
'agroecologyItems' => [
['label' => 'Sumber Air Lahan', 'value' => $this->sourceWaterSummary($filters), 'status' => 'available'],
['label' => 'Pengelolaan Air & Irigasi', 'value' => $this->waterManagementSummary($filters), 'status' => 'available'],
['label' => 'Kebutuhan Air per Musim', 'value' => $this->seasonalWaterNeedSummary($filters), 'status' => 'available'],
['label' => 'Praktik Konservasi Tanah', 'value' => $this->soilConservationSummary($filters), 'status' => 'available'],
],
'agroforestryCards' => [
[
'value' => number_format($tanamanPenaung['total'], 0, ',', '.'),
'label' => 'Jumlah Tanaman Penaung',
'description' => $tanamanPenaung['types'].' jenis tanaman penaung',
'icon' => 'tabler-leaf',
'variant' => 'success',
],
[
'value' => number_format($tanamanPendamping['types'], 0, ',', '.'),
'label' => 'Jenis Tanaman Pendamping',
'description' => number_format($tanamanPendamping['total'], 0, ',', '.').' entri tanaman pendamping',
'icon' => 'tabler-flower',
'variant' => 'warning',
],
[
'value' => number_format(round($diversityAverage), 0, ',', '.'),
'label' => 'Tingkat Campuran Tanaman',
'description' => 'Rata-rata kombinasi tanaman utama, pendamping, dan penaung',
'icon' => 'tabler-binary-tree',
'variant' => 'primary',
],
],
];
}
private function summaryData(array $filters): array
{
$totalPetani = $this->applyPetaniFilters(Petani::query(), $filters)->count();
@@ -166,6 +338,10 @@ class IndikatorPertanianController extends Controller
'statusPanen' => $this->statusPanenChart($filters),
'surveyProduktivitasHa' => $this->surveyProduktivitasByUnitChart($filters, 'ha_tahun'),
'surveyProduktivitasPohon' => $this->surveyProduktivitasByUnitChart($filters, 'pohon_tahun'),
'luasLahanPerKomoditas' => $this->luasLahanPerKomoditasChart($filters),
'jumlahTanamanPerKomoditas' => $this->jumlahTanamanPerKomoditasChart($filters),
'umurTanaman' => $this->umurTanamanChart($filters),
'sumberAir' => $this->sumberAirChart($filters),
];
}
@@ -355,6 +531,82 @@ class IndikatorPertanianController extends Controller
];
}
private function luasLahanPerKomoditasChart(array $filters): array
{
$rows = $this->applyLahanFilters(Lahan::query(), $filters)
->leftJoin('master_komoditas as k', 'k.id', '=', 'map_lahan.komoditas_id')
->whereNotNull('map_lahan.luas_lahan')
->selectRaw("COALESCE(k.nama, 'Belum ditentukan') as label")
->selectRaw('SUM(map_lahan.luas_lahan) as total')
->groupBy('k.id', 'k.nama')
->havingRaw('SUM(map_lahan.luas_lahan) > 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 jumlahTanamanPerKomoditasChart(array $filters): array
{
$rows = $this->applyLahanFilters(Lahan::query(), $filters)
->leftJoin('master_komoditas as k', 'k.id', '=', 'map_lahan.komoditas_id')
->whereNotNull('map_lahan.jumlah_tanaman')
->selectRaw("COALESCE(k.nama, 'Belum ditentukan') as label")
->selectRaw('SUM(map_lahan.jumlah_tanaman) as total')
->groupBy('k.id', 'k.nama')
->havingRaw('SUM(map_lahan.jumlah_tanaman) > 0')
->orderByDesc('total')
->get();
return [
'labels' => $rows->pluck('label')->values()->all(),
'series' => $rows->pluck('total')->map(fn ($value) => (int) $value)->values()->all(),
];
}
private function umurTanamanChart(array $filters): array
{
$bucketOrder = ['< 3 tahun', '3-7 tahun', '8-15 tahun', '> 15 tahun'];
$rows = $this->applyLahanFilters(Lahan::query(), $filters)
->whereNotNull('umur_tanaman')
->pluck('umur_tanaman')
->map(function ($age) {
$age = (float) $age;
return match (true) {
$age < 3 => '< 3 tahun',
$age <= 7 => '3-7 tahun',
$age <= 15 => '8-15 tahun',
default => '> 15 tahun',
};
})
->countBy()
->sortBy(fn ($value, $label) => array_search($label, $bucketOrder, true));
return [
'labels' => $rows->keys()->values()->all(),
'series' => $rows->values()->map(fn ($value) => (int) $value)->values()->all(),
];
}
private function sumberAirChart(array $filters): array
{
$rows = $this->applyPraktikBudidayaFilters(PraktikBudidaya::query(), $filters)
->get(['sumber_air'])
->flatMap(fn (PraktikBudidaya $praktik) => $this->arrayValues($praktik->sumber_air))
->filter()
->countBy()
->sortDesc();
return [
'labels' => $rows->keys()->values()->all(),
'series' => $rows->values()->map(fn ($value) => (int) $value)->values()->all(),
];
}
private function kondisiTanamanChart(array $filters): array
{
$rows = $this->applyTanamanTableFilters(DB::table('map_tanaman'), $filters)
@@ -572,6 +824,415 @@ class IndikatorPertanianController extends Controller
];
}
private function tanamanProduktifCount(array $filters): int
{
return (int) $this->applyTanamanTableFilters(DB::table('map_tanaman'), $filters)
->whereNull('map_tanaman.deleted_at')
->where('map_tanaman.kondisi_tanaman', 'baik')
->count('map_tanaman.id');
}
private function batchPanenCount(array $filters): int
{
$produksiCount = $this->applyProduksiFilters(Produksi::query(), $filters)->count();
$surveyPanenCount = $this->applySurveyPanenFilters(SurveyPanen::query(), $filters)->count();
return (int) $produksiCount + (int) $surveyPanenCount;
}
private function productionSummaryDescription(array $filters): string
{
$chart = $this->produksiPerKomoditasChart($filters);
if (empty($chart['series'])) {
return 'Belum ada produksi aktual per komoditas';
}
return collect($chart['labels'])
->map(fn ($label, $index) => $label.': '.number_format((float) ($chart['series'][$index] ?? 0), 0, ',', '.').' kg')
->take(3)
->implode(' | ');
}
private function commodityFocusData(array $filters): array
{
$focusCommodities = ['Kopi', 'Kakao', 'Vanili', 'Kemiri'];
$rows = $this->applyLahanFilters(Lahan::query(), $filters)
->leftJoin('master_komoditas as k', 'k.id', '=', 'map_lahan.komoditas_id')
->whereIn(DB::raw('LOWER(k.nama)'), collect($focusCommodities)->map(fn ($name) => strtolower($name))->all())
->selectRaw('k.nama as label')
->selectRaw('COUNT(map_lahan.id) as total')
->groupBy('k.nama')
->get()
->mapWithKeys(fn ($row) => [strtolower((string) $row->label) => (int) $row->total]);
return collect($focusCommodities)
->map(fn ($name) => [
'label' => $name,
'total' => $rows[strtolower($name)] ?? 0,
])
->values()
->all();
}
private function farmMapFeatures(array $filters): array
{
return $this->applyLahanFilters(
Lahan::query()->with(['desaKelurahan.kecamatan', 'komoditas']),
$filters
)
->where(function ($query) {
$query
->whereNotNull('lat')
->orWhereNotNull('long')
->orWhereNotNull('path');
})
->limit(250)
->get()
->map(fn (Lahan $lahan) => [
'id' => $lahan->id,
'name' => $lahan->nama,
'commodity' => $lahan->komoditas?->nama ?? 'Belum ditentukan',
'area' => (float) $lahan->luas_lahan,
'village' => $lahan->desaKelurahan?->nama,
'district' => $lahan->desaKelurahan?->kecamatan?->nama,
'lat' => is_numeric($lahan->lat) ? (float) $lahan->lat : null,
'lng' => is_numeric($lahan->long) ? (float) $lahan->long : null,
'path' => is_array($lahan->path) ? $lahan->path : null,
])
->values()
->all();
}
private function mapLevelData(array $filters): array
{
$lahanSummaryByDesa = $this->applyLahanFilters(Lahan::query(), $filters)
->whereNotNull('desa_kelurahan_id')
->select('desa_kelurahan_id')
->selectRaw('COUNT(*) as total_lahan')
->selectRaw('COALESCE(SUM(luas_lahan), 0) as total_luas')
->groupBy('desa_kelurahan_id')
->get();
$desaIds = $lahanSummaryByDesa->pluck('desa_kelurahan_id')->filter()->unique()->values();
if ($filters['desa'] !== 'all') {
$desaIds->push($filters['desa']);
}
$desaList = DesaKelurahan::query()
->select('id', 'nama', 'path', 'kecamatan_id')
->when($desaIds->isNotEmpty(), fn ($query) => $query->whereIn('id', $desaIds->unique()->values()))
->with([
'kecamatan:id,nama,path,kabupaten_kota_id',
'kecamatan.kabupatenKota:id,nama,path,provinsi_id',
'kecamatan.kabupatenKota.provinsi:id,nama,path',
])
->get()
->keyBy('id');
$geoProvinsi = [];
$geoKabupaten = [];
$geoKecamatan = [];
$geoDesa = [];
$geoLahan = [];
foreach ($lahanSummaryByDesa as $summary) {
$desa = $desaList->get($summary->desa_kelurahan_id);
$kec = $desa?->kecamatan;
$kab = $kec?->kabupatenKota;
$prov = $kab?->provinsi;
$totalLahan = (int) $summary->total_lahan;
$totalLuas = (float) $summary->total_luas;
if ($prov && $prov->path) {
$this->accumulateGeoLevel($geoProvinsi, $prov->id, null, $prov->nama, $prov->path, $totalLahan, $totalLuas);
}
if ($kab && $kab->path) {
$this->accumulateGeoLevel($geoKabupaten, $kab->id, $prov?->id, $kab->nama, $kab->path, $totalLahan, $totalLuas);
}
if ($kec && $kec->path) {
$this->accumulateGeoLevel($geoKecamatan, $kec->id, $kab?->id, $kec->nama, $kec->path, $totalLahan, $totalLuas);
}
if ($desa && $desa->path) {
$this->accumulateGeoLevel($geoDesa, $desa->id, $kec?->id, $desa->nama, $desa->path, $totalLahan, $totalLuas);
}
}
$this->ensureSelectedRegionOnMap($filters, $geoProvinsi, $geoKabupaten, $geoKecamatan, $geoDesa);
$mapLahan = $this->applyLahanFilters(
Lahan::query()->with([
'profile:id,nama',
'petani:id,nama',
'komoditas:id,nama',
'tanaman:id,lahan_id,komoditas_id',
'tanaman.komoditas:id,nama',
]),
$filters
)
->whereNotNull('path')
->orderByDesc('updated_at')
->limit(500)
->get();
foreach ($mapLahan as $lahan) {
if (! $this->hasMapPolygon($lahan->path)) {
continue;
}
$tanaman = $lahan->tanaman
->map(fn ($tanaman) => $tanaman->komoditas?->nama)
->filter()
->unique()
->values();
if ($tanaman->isEmpty() && $lahan->komoditas?->nama) {
$tanaman = collect([$lahan->komoditas->nama]);
}
$geoLahan[] = [
'id' => $lahan->id,
'parent_id' => $lahan->desa_kelurahan_id,
'polygon' => $lahan->path,
'nama_petani' => $lahan->petani?->nama ?? $lahan->profile?->nama ?? 'N/A',
'luas' => (float) $lahan->luas_lahan,
'tanaman' => $tanaman,
];
}
return [
'provinsi' => array_values($geoProvinsi),
'kabupaten' => array_values($geoKabupaten),
'kecamatan' => array_values($geoKecamatan),
'desa' => array_values($geoDesa),
'lahan' => $geoLahan,
];
}
private function accumulateGeoLevel(array &$bucket, int|string $id, int|string|null $parentId, string $name, mixed $polygon, int $totalLahan, float $totalLuas): void
{
if (! isset($bucket[$id])) {
$bucket[$id] = [
'id' => $id,
'parent_id' => $parentId,
'nama' => $name,
'polygon' => $polygon,
'total_lahan' => 0,
'total_luas' => 0,
];
}
$bucket[$id]['total_lahan'] += $totalLahan;
$bucket[$id]['total_luas'] += $totalLuas;
}
private function ensureSelectedRegionOnMap(array $filters, array &$geoProvinsi, array &$geoKabupaten, array &$geoKecamatan, array &$geoDesa): void
{
$kabupaten = $filters['kabupaten'] !== 'all'
? KabupatenKota::query()->with('provinsi:id,nama,path')->find($filters['kabupaten'])
: null;
if ($kabupaten && $kabupaten->path && ! isset($geoKabupaten[$kabupaten->id])) {
$this->accumulateGeoLevel($geoKabupaten, $kabupaten->id, $kabupaten->provinsi_id, $kabupaten->nama, $kabupaten->path, 0, 0);
}
if ($kabupaten?->provinsi && $kabupaten->provinsi->path && ! isset($geoProvinsi[$kabupaten->provinsi->id])) {
$this->accumulateGeoLevel($geoProvinsi, $kabupaten->provinsi->id, null, $kabupaten->provinsi->nama, $kabupaten->provinsi->path, 0, 0);
}
if ($filters['kecamatan'] !== 'all') {
$kecamatan = Kecamatan::query()->find($filters['kecamatan']);
if ($kecamatan && $kecamatan->path && ! isset($geoKecamatan[$kecamatan->id])) {
$this->accumulateGeoLevel($geoKecamatan, $kecamatan->id, $kecamatan->kabupaten_kota_id, $kecamatan->nama, $kecamatan->path, 0, 0);
}
}
if ($filters['desa'] !== 'all') {
$desa = DesaKelurahan::query()->find($filters['desa']);
if ($desa && $desa->path && ! isset($geoDesa[$desa->id])) {
$this->accumulateGeoLevel($geoDesa, $desa->id, $desa->kecamatan_id, $desa->nama, $desa->path, 0, 0);
}
}
}
private function hasMapPolygon(mixed $path): bool
{
if (is_array($path)) {
return ! empty($path);
}
if (! is_string($path) || blank($path) || strtolower($path) === 'null') {
return false;
}
$decoded = json_decode($path, true);
return json_last_error() === JSON_ERROR_NONE && ! empty($decoded) && $decoded !== 'null';
}
private function shadowTreeSummary(array $filters): array
{
$items = $this->applyLahanFilters(Lahan::query(), $filters)
->get(['tanaman_penaung'])
->flatMap(fn (Lahan $lahan) => $this->arrayValues($lahan->tanaman_penaung))
->filter()
->values();
return [
'total' => $items->count(),
'types' => $items->unique(fn ($item) => strtolower((string) $item))->count(),
];
}
private function companionPlantSummary(array $filters): array
{
$items = $this->applyLahanFilters(Lahan::query(), $filters)
->get(['id', 'komoditas_lain'])
->flatMap(fn (Lahan $lahan) => $this->arrayValues($lahan->komoditas_lain))
->filter()
->values();
return [
'total' => $items->count(),
'types' => $items->unique(fn ($item) => strtolower((string) $item))->count(),
];
}
private function plantDiversityAverage(array $filters): float
{
$lahan = $this->applyLahanFilters(Lahan::query(), $filters)
->get(['komoditas_id', 'komoditas_lain', 'tanaman_penaung']);
if ($lahan->isEmpty()) {
return 0;
}
return (float) $lahan->avg(function (Lahan $item) {
$total = $item->komoditas_id ? 1 : 0;
$total += count($this->arrayValues($item->komoditas_lain));
$total += count($this->arrayValues($item->tanaman_penaung));
return $total;
});
}
private function sourceWaterSummary(array $filters): string
{
$chart = $this->sumberAirChart($filters);
if (empty($chart['labels'])) {
return 'Belum ada data';
}
return collect($chart['labels'])
->map(fn ($label, $index) => $label.' ('.number_format((int) ($chart['series'][$index] ?? 0), 0, ',', '.').')')
->take(2)
->implode(', ');
}
private function waterManagementSummary(array $filters): string
{
$rows = $this->applyPraktikBudidayaFilters(PraktikBudidaya::query(), $filters)
->whereNotNull('kondisi_pengelolaan_air')
->pluck('kondisi_pengelolaan_air')
->map(fn ($value) => $this->waterManagementShortLabel((int) $value))
->countBy()
->sortDesc();
return $this->topCountSummary($rows);
}
private function seasonalWaterNeedSummary(array $filters): string
{
$rows = $this->applyPraktikBudidayaFilters(PraktikBudidaya::query(), $filters)
->whereNotNull('kebutuhan_air_permusim')
->pluck('kebutuhan_air_permusim')
->map(fn ($value) => $this->shortEnumLabel((string) $value))
->countBy()
->sortDesc();
return $this->topCountSummary($rows);
}
private function soilConservationSummary(array $filters): string
{
$rows = $this->applyPraktikBudidayaFilters(PraktikBudidaya::query(), $filters)
->get(['kebiasaan_konservasi_tanah'])
->flatMap(fn (PraktikBudidaya $praktik) => $this->arrayValues($praktik->kebiasaan_konservasi_tanah))
->filter()
->countBy()
->sortDesc();
return $this->topCountSummary($rows);
}
private function soilConservationScoreSummary(array $filters): string
{
$average = $this->applyPraktikBudidayaFilters(PraktikBudidaya::query(), $filters)
->whereNotNull('nilai_konservasi_tanah')
->avg('nilai_konservasi_tanah');
if ($average === null) {
return 'Belum ada data';
}
return 'Rata-rata '.number_format((float) $average, 1, ',', '.');
}
private function topCountSummary($rows): string
{
if ($rows->isEmpty()) {
return 'Belum ada data';
}
return $rows
->map(fn ($count, $label) => $label.' ('.number_format((int) $count, 0, ',', '.').')')
->take(2)
->implode(', ');
}
private function waterManagementShortLabel(int $value): string
{
return match (KondisiPengelolaanAir::tryFrom($value)) {
KondisiPengelolaanAir::KURANG_TIDAK_ADA_UPAYA => 'Kurang, tanpa upaya',
KondisiPengelolaanAir::KURANG_TIDAK_SUDAH_BERUPAYA => 'Kurang, ada upaya',
KondisiPengelolaanAir::CUKUP_DARI_HUJAN => 'Cukup dari hujan',
KondisiPengelolaanAir::CUKUP_IRIGASI_PINJAM => 'Cukup, irigasi pinjam',
KondisiPengelolaanAir::CUKUP_IRIGASI_SENDIRI => 'Cukup, irigasi sendiri',
default => 'Belum ditentukan',
};
}
private function arrayValues(mixed $value): array
{
if (is_array($value)) {
return collect($value)
->flatten()
->filter(fn ($item) => filled($item))
->map(fn ($item) => is_scalar($item) ? (string) $item : json_encode($item))
->values()
->all();
}
if (is_string($value) && filled($value) && strtolower($value) !== 'null') {
$decoded = json_decode($value, true);
if (json_last_error() === JSON_ERROR_NONE) {
return $this->arrayValues($decoded);
}
return [$value];
}
return [];
}
private function chartFromRows($rows, callable $labelResolver): array
{
return [
@@ -676,6 +1337,13 @@ class IndikatorPertanianController extends Controller
->when($filters['komoditas'] !== 'all', fn ($query) => $query->where('komoditas_id', $filters['komoditas']));
}
private function applySurveyPanenFilters($query, array $filters)
{
return $query->whereHas('petani', function ($petani) use ($filters) {
$this->applyPetaniFilters($petani, $filters);
});
}
private function applyTanamanTableFilters($query, array $filters)
{
return $query
@@ -0,0 +1,210 @@
<?php
namespace App\Http\Controllers\Web\Dashboard;
use App\Http\Controllers\Controller;
use App\Services\AlphaVantageMarket;
use Illuminate\Contracts\Cache\Repository;
use Illuminate\Http\Client\Pool;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Http;
use Throwable;
class InternationalMarketController extends Controller
{
public function __invoke()
{
abort_unless(Gate::allows('super_admin') || Gate::allows('admin') || Gate::allows('fasilitator'), 403);
// File cache keeps external quotes independent of the application database.
$cache = Cache::store('file');
$marketData = $cache->remember('market.yahoo.quotes.v3', 300, function () use ($cache) {
$commodities = [
['name' => 'Kopi Arabica', 'symbol' => 'KC=F'],
['name' => 'Kakao (Cocoa)', 'symbol' => 'CC=F'],
];
try {
$responses = Http::pool(function (Pool $pool) use ($commodities) {
return array_map(fn ($commodity) => $pool->as($commodity['symbol'])
->acceptJson()->withHeaders(['User-Agent' => 'Mozilla/5.0'])
->withOptions(['verify' => config('services.yahoo_finance.ca_bundle') ?: true])
->connectTimeout(3)->timeout(8)
->get('https://query1.finance.yahoo.com/v8/finance/chart/'.rawurlencode($commodity['symbol']), [
'interval' => '1d', 'range' => '5d',
]), [...$commodities, ['symbol' => 'IDR=X']]);
});
} catch (Throwable $exception) {
report($exception);
$responses = [];
}
$exchangeRate = $cache->get('market.yahoo.usd_idr');
if (is_array($exchangeRate)) {
$exchangeRate['stale'] = true;
}
$rateResponse = $responses['IDR=X'] ?? null;
if ($rateResponse instanceof \Illuminate\Http\Client\Response && $rateResponse->successful()) {
$meta = $rateResponse->json('chart.result.0.meta');
if (is_array($meta) && ($meta['symbol'] ?? null) === 'IDR=X'
&& ($meta['currency'] ?? null) === 'IDR'
&& is_numeric($meta['regularMarketPrice'] ?? null) && $meta['regularMarketPrice'] > 0
&& is_numeric($meta['regularMarketTime'] ?? null)) {
$exchangeRate = [
'value' => (float) $meta['regularMarketPrice'],
'quoted_at' => gmdate('c', (int) $meta['regularMarketTime']),
'source' => 'Yahoo Finance',
'source_url' => 'https://finance.yahoo.com/quote/IDR%3DX/',
'stale' => false,
];
$cache->put('market.yahoo.usd_idr', $exchangeRate, now()->addDays(7));
}
}
$quotes = array_map(function ($commodity) use ($responses, $cache) {
$symbol = $commodity['symbol'];
$key = 'market.yahoo.last.'.rawurlencode($symbol);
$quote = $commodity + [
'source' => 'Yahoo Finance',
'source_url' => 'https://finance.yahoo.com/quote/'.rawurlencode($symbol).'/',
'price' => null, 'currency' => null, 'unit' => null,
'quoted_at' => null, 'stale' => false,
];
try {
$response = $responses[$symbol] ?? null;
if (!$response instanceof \Illuminate\Http\Client\Response || !$response->successful()) {
throw new \RuntimeException('Quote unavailable');
}
$meta = $response->json('chart.result.0.meta');
if ($response->json('chart.error') || !is_array($meta)
|| ($meta['symbol'] ?? null) !== $symbol
|| !is_numeric($meta['regularMarketPrice'] ?? null)
|| (float) $meta['regularMarketPrice'] <= 0
|| !is_numeric($meta['regularMarketTime'] ?? null)
|| !is_string($meta['currency'] ?? null)) {
throw new \RuntimeException('Invalid quote');
}
$quote['price'] = (float) $meta['regularMarketPrice'];
$quote['currency'] = $meta['currency'];
// Yahoo quotes Arabica in US cents/lb; cocoa and robusta in USD/tonne.
// Apply the unit only when the API currency matches that convention.
$quote['unit'] = match (true) {
$symbol === 'KC=F' && $meta['currency'] === 'USX' => 'sen AS / lb',
$symbol === 'CC=F' && $meta['currency'] === 'USD' => 'USD / ton metrik',
default => $meta['currency'].' (satuan mengikuti sumber)',
};
$quote['quoted_at'] = gmdate('c', (int) $meta['regularMarketTime']);
$cache->put($key, $quote, now()->addDays(7));
return $quote;
} catch (Throwable $exception) {
$previous = $cache->get($key);
if (is_array($previous)) {
$previous['stale'] = true;
return $previous;
}
return $quote;
}
}, $commodities);
return ['quotes' => $quotes, 'exchange_rate' => $exchangeRate];
});
$quotes = $marketData['quotes'];
$exchangeRate = $marketData['exchange_rate'];
foreach ($quotes as &$quote) {
$quote['id'] = 'yahoo:'.$quote['symbol'];
$quote['description'] = 'Estimasi konversi harga kontrak berjangka internasional.';
$quote['period'] = $quote['quoted_at'];
$quote['frequency'] = 'market';
}
unset($quote);
$quotes = array_merge($quotes, $this->imfQuotes($cache), app(AlphaVantageMarket::class)->quotes($cache));
$quotes = array_values(array_filter($quotes, fn ($quote) => is_numeric($quote['price']) && is_finite((float) $quote['price']) && $quote['price'] > 0));
foreach ($quotes as &$quote) {
// All active providers quote either US cents/lb or USD/metric tonne.
$usdPerKg = match ($quote['unit']) {
'sen AS / lb' => $quote['price'] / 100 / 0.45359237,
'USD / ton metrik' => $quote['price'] / 1000,
default => null,
};
$quote['price_idr_per_kg'] = $exchangeRate && $usdPerKg !== null
? round($usdPerKg * $exchangeRate['value'], 2) : null;
unset($quote['usd_per_kg']); // Old Alpha cache entries may contain this field.
}
unset($quote);
return response()->json([
'quotes' => $quotes,
'exchange_rate' => $exchangeRate,
])->header('Cache-Control', 'no-store');
}
private function imfQuotes(Repository $cache): array
{
// IMF observations are monthly averages; fetch at most once a day.
return $cache->remember('market.imf.monthly.v1', 86400, function () use ($cache) {
try {
$response = Http::withHeaders(['Accept' => 'text/csv', 'User-Agent' => 'Mozilla/5.0'])
->withOptions(['verify' => config('services.yahoo_finance.ca_bundle') ?: true])
->connectTimeout(3)->timeout(10)
->get('https://api.imf.org/external/sdmx/2.1/data/IMF.RES,PCPS/G001.PCOFFOTM+PCOFFROB+PCOCO.USD.M', [
'startPeriod' => now()->subYears(2)->format('Y-m'), 'format' => 'csvfile',
])->throw();
$stream = fopen('php://memory', 'r+');
try {
fwrite($stream, $response->body());
rewind($stream);
$headers = fgetcsv($stream, 0, ',', '"', '');
if (!$headers || array_diff(['COUNTRY', 'INDICATOR', 'DATA_TRANSFORMATION', 'FREQUENCY', 'TIME_PERIOD', 'OBS_VALUE', 'SCALE'], $headers)) {
throw new \RuntimeException('Unexpected IMF CSV format');
}
$latest = [];
while (($values = fgetcsv($stream, 0, ',', '"', '')) !== false) {
if (count($values) !== count($headers)) continue;
$row = array_combine($headers, $values);
if ($row['COUNTRY'] !== 'G001' || $row['DATA_TRANSFORMATION'] !== 'USD' || $row['FREQUENCY'] !== 'M'
|| $row['SCALE'] !== '0' || !is_numeric($row['OBS_VALUE']) || $row['OBS_VALUE'] <= 0
|| !preg_match('/^\d{4}-M(0[1-9]|1[0-2])$/', $row['TIME_PERIOD'])) continue;
$indicator = $row['INDICATOR'];
if (!isset($latest[$indicator]) || $row['TIME_PERIOD'] > $latest[$indicator]['TIME_PERIOD']) {
$latest[$indicator] = $row;
}
}
} finally {
fclose($stream);
}
$definitions = [
'PCOFFOTM' => ['Kopi Arabica', 'USX', 'sen AS / lb', 'Biji kopi Arabica (Other Milds). Estimasi konversi harga acuan rata-rata bulanan IMF.'],
'PCOFFROB' => ['Kopi Robusta', 'USX', 'sen AS / lb', 'Biji kopi Robusta. Estimasi konversi harga acuan rata-rata bulanan IMF.'],
'PCOCO' => ['Kakao (Cocoa)', 'USD', 'USD / ton metrik', 'Biji kakao. Estimasi konversi harga acuan rata-rata bulanan IMF.'],
];
$quotes = [];
foreach ($definitions as $indicator => [$name, $currency, $unit, $description]) {
if (!isset($latest[$indicator])) continue;
$row = $latest[$indicator];
$quotes[] = [
'id' => 'imf:'.$indicator, 'symbol' => $indicator, 'name' => $name,
'price' => (float) $row['OBS_VALUE'], 'currency' => $currency, 'unit' => $unit,
'source' => 'IMF · PCPS', 'source_url' => 'https://data.imf.org/Datasets/PCPS',
'description' => $description, 'frequency' => 'monthly',
'period' => str_replace('-M', '-', $row['TIME_PERIOD']), 'stale' => false,
];
}
if (!$quotes) throw new \RuntimeException('No valid IMF observations');
$cache->put('market.imf.last.v1', $quotes, now()->addDays(30));
return $quotes;
} catch (Throwable $exception) {
report($exception);
return array_map(function ($quote) {
$quote['stale'] = true;
return $quote;
}, $cache->get('market.imf.last.v1', []));
}
});
}
}
File diff suppressed because it is too large Load Diff
@@ -489,34 +489,34 @@ class SurveyPetaniController extends Controller
{
return [
[
'color' => '#28a745',
'bg' => 'rgba(40,167,69,.15)',
'tx' => '#28a745',
'color' => '#6FD68F',
'bg' => 'rgba(111,214,143,.24)',
'tx' => '#6FD68F',
],
[
'color' => '#007bff',
'bg' => 'rgba(0,123,255,.15)',
'tx' => '#007bff',
'color' => '#5FC7D6',
'bg' => 'rgba(95,199,214,.24)',
'tx' => '#5FC7D6',
],
[
'color' => '#fd7e14',
'bg' => 'rgba(253,126,20,.15)',
'tx' => '#fd7e14',
'color' => '#FFB46F',
'bg' => 'rgba(255,180,111,.26)',
'tx' => '#FFB46F',
],
[
'color' => '#dc3545',
'bg' => 'rgba(220,53,69,.15)',
'tx' => '#dc3545',
'color' => '#F47C86',
'bg' => 'rgba(244,124,134,.26)',
'tx' => '#F47C86',
],
[
'color' => '#6f42c1',
'bg' => 'rgba(111,66,193,.15)',
'tx' => '#6f42c1',
'color' => '#9D8EF0',
'bg' => 'rgba(157,142,240,.26)',
'tx' => '#9D8EF0',
],
[
'color' => '#20c997',
'bg' => 'rgba(32,201,151,.15)',
'tx' => '#20c997',
'color' => '#65D8BD',
'bg' => 'rgba(101,216,189,.26)',
'tx' => '#65D8BD',
],
];
}
@@ -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();
@@ -24,7 +24,7 @@ class AksesPasarController extends Controller
{
$petani = Petani::findOrFail($petaniId);
$aksesPasar = AksesPasar::where('petani_id', $petaniId)->first();
$aksesPasar = AksesPasar::where('petani_id', $petaniId)->latest()->first();
$hargaJualPanen = HargaJualPanen::where('petani_id', $petaniId)->get();
$hargaJualRows = collect([
'Buah segar hasil petik',
@@ -50,32 +50,40 @@ class AksesPasarController extends Controller
}
public function store(Request $request)
{
$validated = $this->validateData($request);
{
$validated = $this->validateData($request);
DB::beginTransaction();
DB::beginTransaction();
try {
$aksesPasarData = $this->aksesPasarPayload($validated);
try {
$aksesPasarData = $this->aksesPasarPayload($validated);
$existingAksesPasar = AksesPasar::where('petani_id', $validated['petani_id'])->first();
if ($existingAksesPasar) {
$aksesPasarData['updated_by'] = Auth::id();
$existingAksesPasar->update($aksesPasarData);
$aksesPasar = $existingAksesPasar;
} else {
$aksesPasarData['uid'] = 'AKP-' . Str::uuid();
$aksesPasarData['created_by'] = Auth::id();
$aksesPasarData['updated_by'] = Auth::id();
$aksesPasar = AksesPasar::create($aksesPasarData);
if ($request->has('harga_jual')) {
$this->syncHargaJual($request->petani_id, $validated['harga_jual'] ?? [], Auth::id());
}
DB::commit();
return redirect()->back()->with('success', 'Data Rantai Nilai dan Akses Pasar berhasil disimpan.');
} catch (\Exception $e) {
DB::rollBack();
return redirect()->back()->withErrors(['error' => 'Gagal menyimpan data: ' . $e->getMessage()])->withInput();
}
if ($request->has('harga_jual')) {
$this->syncHargaJual($request->petani_id, $validated['harga_jual'] ?? [], Auth::id());
}
DB::commit();
return redirect()->back()->with('success', 'Data Rantai Nilai dan Akses Pasar berhasil disimpan.');
} catch (\Exception $e) {
DB::rollBack();
return redirect()->back()->withErrors(['error' => 'Gagal menyimpan data: ' . $e->getMessage()])->withInput();
}
}
public function update(Request $request, $petaniId, $id)
{
@@ -118,7 +126,8 @@ class AksesPasarController extends Controller
'skorPengalamanBerhentiValues' => [
SkorPengalaman::PERNAH_BERHENTI_TIDAK_ADA_AKSES->value,
SkorPengalaman::PERNAH_BERHENTI_MUNGKIN_KEMBALI->value
]
],
'sumberInfoLainnyaValue' => 'Lainnya',
];
}
@@ -128,6 +137,17 @@ class AksesPasarController extends Controller
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'), [
@@ -144,6 +164,14 @@ class AksesPasarController extends Controller
'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),
@@ -168,6 +196,7 @@ class AksesPasarController extends Controller
], [
'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.',
@@ -204,6 +233,10 @@ class AksesPasarController extends Controller
{
$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
@@ -215,6 +248,10 @@ class AksesPasarController extends Controller
$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;
@@ -223,7 +260,6 @@ class AksesPasarController extends Controller
return $payload;
}
private function syncHargaJual($petaniId, array $hargaJualData, $createdBy, $updatedBy = null): void
{
HargaJualPanen::where('petani_id', $petaniId)->delete();
@@ -0,0 +1,186 @@
<?php
namespace App\Http\Controllers\Web\Produktivitas;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\DB;
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, ?string $nextSection = null)
{
return redirect($url . '#section-' . ($nextSection ?? $section))->with([
'success' => $message,
'success_section' => $section,
]);
}
private function kegiatanUrl(KelompokTani $kelompokTani): string
{
return route('kelompok-tani.edit', $kelompokTani->id) . '?section=aktivitas-fungsi';
}
private function nextAssetSection(KelompokTani $kelompokTani): string
{
return $kelompokTani->aset()->where('jenis_aset', 'Kas/simpanan kelompok')->where('status_aset', true)->exists()
? 'kas' : 'status-bantuan';
}
public function index(KelompokTani $kelompokTani)
{
$this->authorizeAccess();
return redirect($this->kegiatanUrl($kelompokTani) . '#section-aset-baku');
}
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' => ['required_if_accepted:aset.*.status_aset', 'nullable', 'string', 'max:100'],
'aset.*.kondisi_aset' => ['required_if_accepted:aset.*.status_aset', 'nullable', 'string', 'max:100'],
...$this->asetLainnyaRules(),
], [
'aset.*.status_pengelolaan_aset.required_if_accepted' => 'Status Pengelolaan wajib diisi jika aset dipilih Ada.',
'aset.*.kondisi_aset.required_if_accepted' => 'Kondisi wajib diisi jika aset dipilih Ada.',
'aset_lainnya.*.jenis_aset.not_in' => 'Jenis aset ini sudah ada di daftar baku di atas.',
]);
DB::transaction(function () use ($kelompokTani, $validated) {
$this->saveAsetBaku($kelompokTani, $validated['aset']);
$this->createAsetLainnya($kelompokTani, $validated['aset_lainnya'] ?? []);
});
return $this->redirectToSection($this->kegiatanUrl($kelompokTani), 'aset-baku', 'Data Operasional berhasil disimpan.', $this->nextAssetSection($kelompokTani));
}
private function saveAsetBaku(KelompokTani $kelompokTani, array $items): void
{
foreach ($items 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'],
'kondisi_aset' => $item['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(),
]);
}
}
private function asetLainnyaRules(): array
{
return [
'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'],
];
}
public function store(Request $request, KelompokTani $kelompokTani)
{
$this->authorizeAccess();
$validated = $request->validate($this->asetLainnyaRules(), [
'aset_lainnya.*.jenis_aset.not_in' => 'Jenis aset ini sudah ada di daftar baku di atas.',
]);
DB::transaction(fn () => $this->createAsetLainnya($kelompokTani, $validated['aset_lainnya'] ?? []));
return $this->redirectToSection($this->kegiatanUrl($kelompokTani), 'aset-lainnya', 'Data Operasional berhasil ditambahkan.', $this->nextAssetSection($kelompokTani));
}
private function createAsetLainnya(KelompokTani $kelompokTani, array $items): void
{
foreach ($items 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(),
]);
}
}
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, 'Operasional: gunakan pilihan Ada/Tidak pada tabel untuk menghapus aset baku.');
$aset->update(['deleted_by' => auth()->id()]);
$aset->delete();
return $this->redirectToSection($this->kegiatanUrl($kelompokTani), 'aset-lainnya', 'Data Operasional berhasil dihapus.');
}
}
@@ -0,0 +1,171 @@
<?php
namespace App\Http\Controllers\Web\Produktivitas;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
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, ?string $nextSection = null)
{
return redirect($url . '#section-' . ($nextSection ?? $section))->with([
'success' => $message,
'success_section' => $section,
]);
}
private function redirectToSectionWithError(string $url, string $section, string $message)
{
return redirect($url . '#kelompok-tani-notifications')->with([
'error' => $message,
'error_section' => $section,
]);
}
public function index(KelompokTani $kelompokTani)
{
$this->authorizeAccess();
return redirect($this->operasionalUrl($kelompokTani) . '#section-status-bantuan');
}
private function operasionalUrl(KelompokTani $kelompokTani): string
{
return route('kelompok-tani.edit', $kelompokTani->id) . '?section=aktivitas-fungsi';
}
private function hasRecordedAid(KelompokTani $kelompokTani): bool
{
return $kelompokTani->bantuan()->whereRaw("TRIM(nama_program) <> ''")->exists();
}
private function bantuanRules(bool $pernah = true, bool $requireFirstRow = false): array
{
$exclude = Rule::excludeIf(!$pernah);
return [
'bantuan' => [$exclude, Rule::requiredIf($requireFirstRow), 'nullable', 'array'],
'bantuan.*' => [$exclude, 'array'],
'bantuan.*.nama_program' => [$exclude, 'required_with:bantuan.*.penyelenggara,bantuan.*.bentuk_bantuan,bantuan.*.tahun', 'nullable', 'string', 'max:255'],
'bantuan.*.penyelenggara' => [$exclude, 'nullable', 'string', 'max:255'],
'bantuan.*.bentuk_bantuan' => [$exclude, 'nullable', 'string', 'max:255'],
'bantuan.*.tahun' => [$exclude, 'nullable', 'integer', 'min:1900', 'max:' . date('Y')],
'bantuan.0.nama_program' => [$exclude, Rule::requiredIf($requireFirstRow), 'required_with:bantuan.0.penyelenggara,bantuan.0.bentuk_bantuan,bantuan.0.tahun', 'nullable', 'string', 'max:255'],
'bantuan.0.penyelenggara' => [$exclude, Rule::requiredIf($requireFirstRow), 'nullable', 'string', 'max:255'],
'bantuan.0.bentuk_bantuan' => [$exclude, Rule::requiredIf($requireFirstRow), 'nullable', 'string', 'max:255'],
'bantuan.0.tahun' => [$exclude, Rule::requiredIf($requireFirstRow), 'nullable', 'integer', 'min:1900', 'max:' . date('Y')],
];
}
private function bantuanMessages(): array
{
return [
'pernah_mendapatkan_bantuan.required' => 'Pengalaman Akses Bantuan/Program wajib diisi.',
'pernah_mendapatkan_bantuan.boolean' => 'Pilih Pernah atau Belum Pernah untuk Pengalaman Akses Bantuan/Program.',
'bantuan.required' => 'Isi minimal satu data bantuan jika memilih Pernah.',
'bantuan.0.nama_program.required' => 'Nama Program/Bantuan pada baris pertama wajib diisi.',
'bantuan.0.penyelenggara.required' => 'Penyelenggara pada baris pertama wajib diisi.',
'bantuan.0.bentuk_bantuan.required' => 'Bentuk Bantuan pada baris pertama wajib diisi.',
'bantuan.0.tahun.required' => 'Tahun pada baris pertama wajib diisi.',
'bantuan.*.nama_program.required_with' => 'Nama Program/Bantuan wajib diisi jika baris bantuan diisi.',
];
}
public function update(Request $request, KelompokTani $kelompokTani)
{
$this->authorizeAccess();
$validated = $request->validate([
'pernah_mendapatkan_bantuan' => ['required', 'boolean'],
...$this->bantuanRules(
$request->boolean('pernah_mendapatkan_bantuan'),
$request->boolean('pernah_mendapatkan_bantuan') && !$this->hasRecordedAid($kelompokTani)
),
], $this->bantuanMessages());
DB::transaction(function () use ($kelompokTani, $validated) {
$kelompokTani->update([
'pernah_mendapatkan_bantuan' => $validated['pernah_mendapatkan_bantuan'],
'updated_by' => auth()->id(),
]);
$this->createBantuan($kelompokTani, $validated['bantuan'] ?? []);
});
return $this->redirectToSection($this->operasionalUrl($kelompokTani), 'status-bantuan', 'Data Operasional berhasil disimpan.');
}
public function store(Request $request, KelompokTani $kelompokTani)
{
$this->authorizeAccess();
if (!$kelompokTani->pernah_mendapatkan_bantuan) {
return $this->redirectToSectionWithError(
$this->operasionalUrl($kelompokTani),
'status-bantuan',
'Operasional: pilih jawaban "Pernah" terlebih dahulu sebelum menambahkan data bantuan/program.'
);
}
$validated = $request->validate($this->bantuanRules(true, !$this->hasRecordedAid($kelompokTani)), $this->bantuanMessages());
DB::transaction(fn () => $this->createBantuan($kelompokTani, $validated['bantuan'] ?? []));
return $this->redirectToSection($this->operasionalUrl($kelompokTani), 'status-bantuan', 'Data Operasional berhasil ditambahkan.');
}
private function createBantuan(KelompokTani $kelompokTani, array $items): void
{
foreach ($items as $item) {
$namaProgram = trim($item['nama_program'] ?? '');
if ($namaProgram === '') {
continue;
}
$kelompokTani->bantuan()->create([
'uid' => 'BKT-' . strtoupper(Str::random(10)),
'nama_program' => $namaProgram,
'penyelenggara' => $item['penyelenggara'] ?? '',
'bentuk_bantuan' => $item['bentuk_bantuan'] ?? '',
'tahun' => $item['tahun'] ?? null,
'created_by' => auth()->id(),
'updated_by' => auth()->id(),
]);
}
}
public function destroy(KelompokTani $kelompokTani, BantuanKelompokTani $bantuan)
{
$this->authorizeAccess();
abort_unless($bantuan->survey_kelompok_tani_id === $kelompokTani->id, 404);
if ($kelompokTani->pernah_mendapatkan_bantuan && !$kelompokTani->bantuan()
->where('id', '!=', $bantuan->id)->whereRaw("TRIM(nama_program) <> ''")->exists()) {
return $this->redirectToSectionWithError(
$this->operasionalUrl($kelompokTani),
'status-bantuan',
'Operasional: minimal satu data bantuan harus tersedia saat jawaban masih Pernah.'
);
}
$bantuan->update(['deleted_by' => auth()->id()]);
$bantuan->delete();
return $this->redirectToSection($this->operasionalUrl($kelompokTani), 'status-bantuan', 'Data Operasional berhasil dihapus.');
}
}
@@ -0,0 +1,79 @@
<?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();
return redirect(route('kelompok-tani.edit', $kelompokTani->id) . '#section-ringkasan');
}
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.edit', $kelompokTani->id), 'pengurus', 'Data Profil 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.edit', $kelompokTani->id), 'pengurus', 'Data Profil berhasil dihapus.');
}
}
@@ -12,6 +12,8 @@ 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;
@@ -35,6 +37,31 @@ class PetaniController extends Controller
}
$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,
};
$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',
@@ -43,29 +70,114 @@ class PetaniController extends Controller
'desaKelurahan.kecamatan.kabupatenKota.provinsi:id,nama',
'komoditas:id,nama',
'skor:id,petani_id,skor_kesiapan_adopsi',
'creator:id,name,role',
])
->orderBy('created_at', 'desc');
->withSum('lahans as total_luas_lahan', 'luas_lahan');
if ($request->search) {
$query->where(function ($query) use ($request) {
$query->where('nama', 'like', '%' . $request->search . '%')
->orWhere('ktp', 'like', '%' . $request->search . '%');
$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 (auth()->user()->role->value === 'fasilitator') {
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);
$komoditas = Komoditas::select('id', 'nama')->where('is_utama', true)->get();
$lahanQuery = Lahan::whereIn('petani_id', $summaryPetaniIds)
->whereIn('komoditas_id', $komoditas->pluck('id'));
$lahanIds = (clone $lahanQuery)->pluck('id');
$totalLuasLahan = (clone $lahanQuery)->sum('luas_lahan');
$totalTanaman = Tanaman::whereIn('lahan_id', $lahanIds)->count();
$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
@@ -81,7 +193,25 @@ class PetaniController extends Controller
->groupByRaw($case)
->get();
return view('produktivitas.petani.index', compact('petani', 'provinsi', 'komoditas', 'skor', 'totalLuasLahan', 'totalTanaman'));
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()
@@ -263,12 +393,38 @@ class PetaniController extends Controller
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)
@@ -49,20 +49,43 @@ class ProfilPetaniController extends Controller
$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::where('petani_id', $id )->get();
$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) : '-';
$profiling = $skorPetani ? $this->buildProfiling($skorPetani) : null;
$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) {
if ($tanaman?->updated_at && $tanaman->umur_tanaman !== null) {
$tahunInputTanaman = $tanaman->updated_at->year;
$umurTanaman = (int) $tanaman->umur_tanaman;
$tahunSekarang = now()->year;
@@ -162,8 +185,8 @@ class ProfilPetaniController extends Controller
$d4SosialEkonomi = null;
$terdaftarKelompok = $sosialEkonomi->terdaftar_kelompok_tani;
$peranKelompok = $sosialEkonomi->peran_dalam_kelompok;
$terdaftarKelompok = $sosialEkonomi?->terdaftar_kelompok_tani;
$peranKelompok = $sosialEkonomi?->peran_dalam_kelompok;
if ($sosialEkonomi) {
@@ -207,8 +230,8 @@ class ProfilPetaniController extends Controller
'icon' => 'tabler-minus',
],
default => [
'label' => Str::headline($status),
'short' => Str::headline($status),
'label' => filled($status) ? Str::headline($status) : 'Belum diisi',
'short' => filled($status) ? Str::headline($status) : 'Belum diisi',
'color' => 'secondary',
'icon' => 'tabler-help-circle',
],
@@ -434,12 +457,61 @@ class ProfilPetaniController extends Controller
],
];
// 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,
@@ -447,7 +519,6 @@ class ProfilPetaniController extends Controller
'umurTanaman' => $umurTanaman,
'surveyPanen' => $surveyPanen,
'praktikBudidaya' => $praktikBudidaya,
'praktikBudidaya' => $praktikBudidaya,
'indikatorOnFarm' => $indikatorOnFarm,
'detailOnFarm' => $detailOnFarm,
'sosialEkonomi' => $sosialEkonomi,
@@ -464,6 +535,35 @@ class ProfilPetaniController extends Controller
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) {
@@ -0,0 +1,445 @@
<?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\Wilayah\Provinsi;
use App\Models\Wilayah\KabupatenKota;
use App\Models\Wilayah\Kecamatan;
use App\Models\Wilayah\DesaKelurahan;
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, ?string $nextSection = null)
{
return redirect($url . '#section-' . ($nextSection ?? $section))->with([
'success' => $message,
'success_section' => $section,
]);
}
public function index(Request $request)
{
$validated = $request->validate([
'search' => ['nullable', 'string', 'max:255'],
'kabupaten_kota_id' => ['nullable', 'integer', 'exists:master_kabupaten_kota,id'],
'kecamatan_id' => ['nullable', 'integer', 'exists:master_kecamatan,id'],
'desa_kelurahan_id' => ['nullable', 'integer', 'exists:master_desa_kelurahan,id'],
'komoditas_id' => ['nullable', 'integer', 'exists:master_komoditas,id'],
'jenis_kelompok' => ['nullable', 'string', 'max:50'],
'sort_by' => ['nullable', 'string'],
'per_page' => ['nullable', 'integer', 'in:10,25'],
]);
$defaultKabupaten = KabupatenKota::select('id', 'nama')
->whereRaw('lower(nama) like ?', ['%ngada%'])->orderBy('nama')->first();
$allowedSorts = ['nama_asc', 'nama_desc', 'anggota_desc', 'anggota_asc'];
$filters = [
'search' => $validated['search'] ?? null,
'kabupaten_kota_id' => $validated['kabupaten_kota_id'] ?? $defaultKabupaten?->id,
'kecamatan_id' => $validated['kecamatan_id'] ?? null,
'desa_kelurahan_id' => $validated['desa_kelurahan_id'] ?? null,
'komoditas_id' => $validated['komoditas_id'] ?? null,
'jenis_kelompok' => $validated['jenis_kelompok'] ?? null,
'sort_by' => in_array($validated['sort_by'] ?? null, $allowedSorts, true)
? $validated['sort_by'] : null,
];
$query = KelompokTani::query()
->when(filled($filters['search']), function ($query) use ($filters) {
$search = '%' . Str::lower($filters['search']) . '%';
$query->where(function ($query) use ($search) {
$query->whereRaw('lower(nama_kelompok) like ?', [$search])
->orWhereRaw('lower(nama_responden) like ?', [$search]);
});
})
->when($filters['kabupaten_kota_id'], function ($query, $id) {
$query->whereHas('desaKelurahan.kecamatan', fn ($query) => $query->where('kabupaten_kota_id', $id));
})
->when($filters['kecamatan_id'], function ($query, $id) {
$query->whereHas('desaKelurahan', fn ($query) => $query->where('kecamatan_id', $id));
})
->when($filters['desa_kelurahan_id'], fn ($query, $id) => $query->where('desa_kelurahan_id', $id))
->when($filters['komoditas_id'], function ($query, $id) {
$query->where(function ($query) use ($id) {
$query->whereJsonContains('komoditas_id', (string) $id)
->orWhereJsonContains('komoditas_id', (int) $id);
});
})
->when($filters['jenis_kelompok'], fn ($query, $jenis) => $query->where('jenis_kelompok', $jenis));
$summary = (clone $query)->selectRaw('
COUNT(*) AS total_kelompok,
COUNT(DISTINCT jenis_kelompok) AS total_jenis,
COALESCE(SUM(jumlah_pengurus), 0) AS total_pengurus,
COALESCE(SUM(jumlah_pengurus_aktif), 0) AS pengurus_aktif,
COALESCE(SUM(jumlah_anggota_terdaftar), 0) AS total_anggota,
COALESCE(SUM(jumlah_anggota_aktif), 0) AS anggota_aktif
')->first();
match ($filters['sort_by']) {
'nama_asc' => $query->orderBy('nama_kelompok'),
'nama_desc' => $query->orderByDesc('nama_kelompok'),
'anggota_desc' => $query->orderByRaw('COALESCE(jumlah_anggota_terdaftar, 0) DESC'),
'anggota_asc' => $query->orderByRaw('COALESCE(jumlah_anggota_terdaftar, 0) ASC'),
default => $query->latest(),
};
$perPage = (int) ($validated['per_page'] ?? 10);
$kelompokTani = $query->with('desaKelurahan.kecamatan')->orderByDesc('id')
->paginate($perPage)->appends([...$filters, 'per_page' => $perPage]);
$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 = $filters['kecamatan_id']
? DesaKelurahan::select('id', 'nama')->where('kecamatan_id', $filters['kecamatan_id'])->orderBy('nama')->get()
: collect();
$komoditasOptions = Komoditas::select('id', 'nama')->orderBy('nama')->get();
$jenisKelompokOptions = KelompokTani::query()->whereNotNull('jenis_kelompok')
->where('jenis_kelompok', '!=', '')->distinct()->orderBy('jenis_kelompok')->pluck('jenis_kelompok');
return view('produktivitas.survey-kelompok-tani.index', compact(
'kelompokTani', 'summary', 'filters', 'selectedKabupaten',
'kecamatanOptions', 'desaOptions', 'komoditasOptions', 'jenisKelompokOptions'
));
}
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'),
...$this->profileLocationOptions(),
]);
}
private function profileLocationOptions(?KelompokTani $kelompokTani = null): array
{
$locationId = static fn ($value) => is_scalar($value) && filter_var($value, FILTER_VALIDATE_INT) !== false ? (int) $value : null;
$provinsiId = $locationId(old('provinsi_id', $kelompokTani?->desaKelurahan?->kecamatan?->kabupatenKota?->provinsi_id));
$kabupatenId = $locationId(old('kabupaten_id', $kelompokTani?->desaKelurahan?->kecamatan?->kabupaten_kota_id));
$kecamatanId = $locationId(old('kecamatan_id', $kelompokTani?->desaKelurahan?->kecamatan_id));
return [
'kabupatenOptions' => filled($provinsiId) ? KabupatenKota::where('provinsi_id', $provinsiId)->orderBy('nama')->get() : collect(),
'kecamatanOptions' => filled($kabupatenId) ? Kecamatan::where('kabupaten_kota_id', $kabupatenId)->orderBy('nama')->get() : collect(),
'desaOptions' => filled($kecamatanId) ? DesaKelurahan::where('kecamatan_id', $kecamatanId)->orderBy('nama')->get() : collect(),
];
}
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 Profil berhasil ditambahkan.', 'pengurus');
}
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', [
'kelompokTani' => $kelompokTani->loadMissing(['aset', 'bantuan']),
]),
'sosial-konflik' => view('produktivitas.survey-kelompok-tani.sosial-konflik', compact('kelompokTani')),
default => view('produktivitas.survey-kelompok-tani.edit', [
'kelompokTani' => $kelompokTani->loadMissing('pengurus'),
'provinsi' => Provinsi::orderBy('kode', 'asc')->get(),
'komoditas' => Komoditas::where('is_utama', true)->get(),
...$this->profileLocationOptions($kelompokTani),
]),
};
}
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' => app(BantuanKelompokTaniController::class)->update($request, $kelompokTani),
'penilaian' => redirect($this->editUrl($kelompokTani, 'aktivitas-fungsi') . '#section-status-bantuan'),
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)
{
$validated = $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'],
'provinsi_id' => ['bail', 'required', 'integer', 'exists:master_provinsi,id'],
'kabupaten_id' => ['bail', 'required', 'integer', 'exists:master_kabupaten_kota,id'],
'kecamatan_id' => ['bail', 'required', 'integer', 'exists:master_kecamatan,id'],
'desa_kelurahan_id' => ['bail', 'required', 'integer', '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'],
...$this->keanggotaanRules(),
], [
'nama_responden.required' => 'Nama responden wajib diisi.',
'nama_kelompok.required' => 'Nama kelompok wajib diisi.',
'provinsi_id.required' => 'Provinsi wajib diisi.',
'kabupaten_id.required' => 'Kabupaten/Kota wajib diisi.',
'kecamatan_id.required' => 'Kecamatan wajib diisi.',
'provinsi_id.integer' => 'Pilihan Provinsi tidak valid.',
'kabupaten_id.integer' => 'Pilihan Kabupaten/Kota tidak valid.',
'kecamatan_id.integer' => 'Pilihan Kecamatan tidak valid.',
'desa_kelurahan_id.integer' => 'Pilihan Desa/Kelurahan tidak valid.',
'provinsi_id.exists' => 'Provinsi tidak ditemukan.',
'kabupaten_id.exists' => 'Kabupaten/Kota tidak ditemukan.',
'kecamatan_id.exists' => 'Kecamatan tidak ditemukan.',
'desa_kelurahan_id.required' => 'Desa/Kelurahan wajib dipilih.',
'desa_kelurahan_id.exists' => 'Desa/Kelurahan tidak ditemukan.',
'jenis_kelompok.required' => 'Terdaftar/Dikenal Sebagai wajib diisi.',
'jenis_kelompok_lainnya.required_if' => 'Sebutkan Jenis Kelompok wajib diisi.',
'tahun_berdiri.digits' => 'Tahun berdiri harus 4 digit.',
]);
unset($validated['provinsi_id'], $validated['kabupaten_id'], $validated['kecamatan_id']);
return $validated;
}
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 Profil berhasil disimpan.', 'pengurus');
}
private function keanggotaanRules(): array
{
return [
'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'],
];
}
private function updateRingkasanPengurus(Request $request, KelompokTani $kelompokTani)
{
$validated = $request->validate($this->keanggotaanRules());
$kelompokTani->update([
...$validated,
'updated_by' => auth()->id(),
]);
return $this->redirectToSection($this->editUrl($kelompokTani), 'identitas', 'Data Profil berhasil disimpan.', 'pengurus');
}
private function updateKelembagaan(Request $request, KelompokTani $kelompokTani)
{
$hasSk = in_array($request->input('status_legalitas'), KelompokTani::STATUS_LEGALITAS_DENGAN_SK, true);
$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' => [Rule::excludeIf(!$hasSk), 'nullable', 'string', 'max:100'],
'tanggal_tahun_penerbitan_sk' => [Rule::excludeIf(!$hasSk), 'nullable', 'string', 'max:30'],
]);
$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([
// Frekuensi Pertemuan Rutin
'frekuensi_pertemuan' => ['nullable', 'string', 'max:50'],
'tanggal_pertemuan_terakhir' => ['nullable', 'date'],
// 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 Operasional berhasil disimpan.', 'aset-baku');
}
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($this->editUrl($kelompokTani, 'aktivitas-fungsi'), 'kas', 'Data Operasional berhasil disimpan.', 'status-bantuan');
}
private function updateSosialKonflik(Request $request, KelompokTani $kelompokTani)
{
$pernahKonflik = in_array($request->input('konflik_kelompok'), [
'Pernah, sudah selesai', 'Pernah, masih berlanjut/belum selesai',
], true);
$validated = $request->validate([
// Keterlibatan Perempuan
'partisipasi_perempuan' => ['nullable', 'string', 'max:255'],
// Riwayat Konflik Internal Kelompok
'konflik_kelompok' => ['nullable', 'string', 'max:255'],
'topik_konflik' => [Rule::requiredIf($pernahKonflik), 'nullable', 'string', 'max:255'],
'penyelesaian_konflik' => ['nullable', 'string'],
], [
'topik_konflik.required' => 'Jika Pernah, Soal Apa? wajib diisi jika pernah terjadi konflik internal.',
]);
if (!$pernahKonflik) {
$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 berhasil disimpan.');
}
}
@@ -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');
}
}
+6
View File
@@ -9,6 +9,7 @@ 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
@@ -105,4 +106,9 @@ class PengurusBumdes extends Model
{
return $this->belongsTo(User::class, 'created_by');
}
public function kelembagaanAktor(): HasOne
{
return $this->hasOne(KelembagaanAktorBumdes::class, 'pengurus_bumdes_id');
}
}
+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'
];
}
+10
View File
@@ -22,10 +22,18 @@ 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',
@@ -44,7 +52,9 @@ class AksesPasar extends Model
];
protected $casts = [
'tujuan_penjualan' => 'array',
'saluran_terdahulu' => 'array',
'sumber_info_harga' => 'array',
];
protected static function booted(): void
+37
View File
@@ -0,0 +1,37 @@
<?php
namespace App\Models\Pertanian;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class AsetKelompokTani extends Model
{
use HasFactory, SoftDeletes;
protected $table = 'survey_aset_kelompok_tani';
protected $fillable = [
'uid',
'survey_kelompok_tani_id',
'jenis_aset',
'status_aset',
'status_pengelolaan_aset',
'kondisi_aset',
'created_by',
'updated_by',
'deleted_by',
];
protected $casts = [
'status_aset' => 'boolean',
];
public function kelompokTani(): BelongsTo
{
return $this->belongsTo(KelompokTani::class, 'survey_kelompok_tani_id');
}
}
@@ -0,0 +1,33 @@
<?php
namespace App\Models\Pertanian;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class BantuanKelompokTani extends Model
{
use HasFactory, SoftDeletes;
protected $table = 'survey_bantuan_kelompok_tani';
protected $fillable = [
'uid',
'survey_kelompok_tani_id',
'nama_program',
'penyelenggara',
'bentuk_bantuan',
'tahun',
'created_by',
'updated_by',
'deleted_by',
];
public function kelompokTani(): BelongsTo
{
return $this->belongsTo(KelompokTani::class, 'survey_kelompok_tani_id');
}
}
+141
View File
@@ -0,0 +1,141 @@
<?php
namespace App\Models\Pertanian;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use App\Models\Wilayah\DesaKelurahan;
class KelompokTani extends Model
{
use HasFactory, SoftDeletes;
public const STATUS_LEGALITAS_DENGAN_SK = [
'Sudah ada SK dari desa/kecamatan',
'Sudah ada SK dan terdaftar di Dinas/Kabupaten',
];
protected $table = 'survey_kelompok_tani';
protected $fillable = [
'uid',
// Data Responden
'nama_responden',
'jabatan_responden',
'kontak_responden',
// Identitas Kelompok Tani
'nama_kelompok',
'desa_kelurahan_id',
'dusun',
'tahun_berdiri',
'jenis_kelompok',
'nama_gapoktan',
'komoditas_id',
// Pengurus & Anggota Kelompok
'jumlah_pengurus',
'jumlah_pengurus_aktif',
'jumlah_anggota_terdaftar',
'jumlah_anggota_aktif',
'jumlah_pengurus_laki_laki',
'jumlah_pengurus_perempuan',
'jumlah_anggota_laki_laki',
'jumlah_anggota_perempuan',
'jumlah_anggota_pemuda_laki_laki',
'jumlah_anggota_pemuda_perempuan',
// Cakupan Wilayah Kelompok
'cakupan_wilayah',
'nama_wilayah_cakupan',
// Status Legalitas Kelompok
'status_legalitas',
'nomor_sk',
'tanggal_tahun_penerbitan_sk',
// Kualitas Kepemimpinan
'tingkat_keaktifan_ketua',
'status_pembagian_peran',
'status_dokumentasi_rapat',
// Frekuensi Pertemuan Rutin
'frekuensi_pertemuan',
'tanggal_pertemuan_terakhir',
// Kegiatan Konkret yang Sudah Berjalan
'kegiatan_terlaksana',
'estimasi_panen_minimal',
'estimasi_panen_maksimal',
'satuan_hasil_panen',
'tindakan_hasil_panen',
// Peran & Fungsi Kelompok
'fungsi_kelompok_bagi_anggota',
// Kapasitas Pengelolaan Kas/Simpan Pinjam
'pembukuan_kas',
'pengelola_kas',
'masalah_pengelolaan_kas_simpan_pinjam',
'deskripsi_masalah_pengelolaan_kas_simpan_pinjam',
// Keterlibatan Perempuan
'partisipasi_perempuan',
// Konflik Internal
'konflik_kelompok',
'topik_konflik',
'penyelesaian_konflik',
// Pengalaman Bantuan
'pernah_mendapatkan_bantuan',
// Postensi Sebagai Mitra Agregasi/Pengolahan Kolektif
'potensi_kelompok',
'alasan_penilaian_potensi',
// Audit
'created_by',
'updated_by',
'deleted_by',
];
protected $casts = [
'komoditas_id' => 'array',
'tindakan_hasil_panen' => 'array',
'fungsi_kelompok_bagi_anggota' => 'array',
'kegiatan_terlaksana' => 'array',
'pernah_mendapatkan_bantuan' => 'boolean',
'tanggal_pertemuan_terakhir' => 'date',
'estimasi_panen_minimal' => 'decimal:2',
'estimasi_panen_maksimal' => 'decimal:2',
];
public function desaKelurahan()
{
return $this->belongsTo(DesaKelurahan::class, 'desa_kelurahan_id');
}
public function pengurus()
{
return $this->hasMany(PengurusKelompokTani::class, 'survey_kelompok_tani_id');
}
public function aset()
{
return $this->hasMany(AsetKelompokTani::class, 'survey_kelompok_tani_id');
}
public function bantuan()
{
return $this->hasMany(BantuanKelompokTani::class, 'survey_kelompok_tani_id');
}
}
@@ -0,0 +1,37 @@
<?php
namespace App\Models\Pertanian;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class PengurusKelompokTani extends Model
{
use HasFactory, SoftDeletes;
protected $table = 'survey_pengurus_kelompok_tani';
protected $fillable = [
'uid',
'survey_kelompok_tani_id',
'nama_pengurus',
'jabatan',
'kontak',
'status',
'created_by',
'updated_by',
'deleted_by',
];
protected $casts = [
'status' => 'boolean',
];
public function kelompokTani(): BelongsTo
{
return $this->belongsTo(KelompokTani::class, 'survey_kelompok_tani_id');
}
}
+68
View File
@@ -0,0 +1,68 @@
<?php
namespace App\Services;
use Illuminate\Contracts\Cache\Repository;
use Illuminate\Support\Facades\Http;
use Throwable;
class AlphaVantageMarket
{
public function quotes(Repository $cache): array
{
if (!$key = config('services.alpha_vantage.api_key')) {
return [];
}
// Retain existing cache keys so cleanup does not spend another API request.
$cacheKey = 'market.additional.v1.alpha:COFFEE';
$lastKey = 'market.additional.last.v1.alpha:COFFEE';
if (is_array($quotes = $cache->get($cacheKey))) {
return $quotes;
}
try {
$response = Http::acceptJson()
->withOptions(['verify' => config('services.yahoo_finance.ca_bundle') ?: true])
->connectTimeout(3)->timeout(6)
->get('https://www.alphavantage.co/query', [
'function' => 'COFFEE', 'interval' => 'monthly', 'apikey' => $key,
])->throw();
$data = $response->json();
if (!is_array($data) || ($data['interval'] ?? null) !== 'monthly'
|| strtolower($data['unit'] ?? '') !== 'cents per pound' || !is_array($data['data'] ?? null)) {
throw new \RuntimeException('Invalid Alpha Vantage response');
}
$latest = null;
foreach ($data['data'] as $row) {
if (!is_array($row) || !is_numeric($row['value'] ?? null) || !is_finite((float) $row['value']) || $row['value'] <= 0
|| !is_string($row['date'] ?? null) || !preg_match('/^\d{4}-(0[1-9]|1[0-2])-01$/', $row['date'])) {
continue;
}
if (!$latest || $row['date'] > $latest['date']) $latest = $row;
}
if (!$latest) throw new \RuntimeException('No valid Alpha Vantage price');
$quote = [
'id' => 'alpha:COFFEE', 'name' => 'Kopi Arabica',
'source' => 'Alpha Vantage (data IMF via FRED)',
'source_url' => 'https://www.alphavantage.co/documentation/#coffee',
'price' => (float) $latest['value'], 'currency' => 'USX', 'unit' => 'sen AS / lb',
'period' => substr($latest['date'], 0, 7), 'frequency' => 'monthly', 'stale' => false,
'description' => 'Biji kopi Arabica (Other Milds). Estimasi konversi harga acuan rata-rata bulanan IMF melalui Alpha Vantage.',
'update_info' => 'Data Alpha Vantage diperiksa setiap hari untuk menjaga kuota API. Harga merupakan rata-rata bulanan dari IMF via FRED.',
];
$cache->put($lastKey, $quote, now()->addDays(30));
$cache->put($cacheKey, [$quote], 86400);
return [$quote];
} catch (Throwable) {
// Request exceptions can contain the API key in their URL; do not log them.
$quotes = [];
if (is_array($last = $cache->get($lastKey))) {
$last['stale'] = true;
$quotes[] = $last;
}
$cache->put($cacheKey, $quotes, 3600);
return $quotes;
}
}
}
+6
View File
@@ -14,6 +14,12 @@ return [
|
*/
'alpha_vantage' => ['api_key' => env('ALPHA_VANTAGE_API_KEY')],
'yahoo_finance' => [
'ca_bundle' => env('YAHOO_FINANCE_CA_BUNDLE'),
],
'postmark' => [
'key' => env('POSTMARK_API_KEY'),
],
@@ -61,7 +61,7 @@ return new class extends Migration
$table->integer('harga_tahun_lalu')->nullable();
$table->integer('harga_dua_tahun_lalu')->nullable();
$table ->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null');
$table ->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null');
$table ->foreignId('updated_by')->nullable()->constrained('users')->onDelete('set null');
$table ->foreignId('deleted_by')->nullable()->constrained('users')->onDelete('set null');
@@ -0,0 +1,142 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('survey_kelompok_tani', function (Blueprint $table) {
$table->id();
$table->string('uid')->unique();
// Data Responden
$table->string('nama_responden')->nullable();
$table->string('jabatan_responden')->nullable();
$table->string('kontak_responden')->nullable();
// Identitas Kelompok Tani
$table->string('nama_kelompok')->nullable();
$table->foreignId('desa_kelurahan_id')->nullable()->constrained('master_desa_kelurahan');
$table->string('dusun')->nullable();
$table->year('tahun_berdiri')->nullable();
$table->string('jenis_kelompok', 50)->nullable();
$table->string('nama_gapoktan')->nullable();
$table->jsonb('komoditas_id')->nullable();
// Pengurus & Anggota Kelompok
$table->unsignedInteger('jumlah_pengurus')->nullable();
$table->unsignedInteger('jumlah_pengurus_aktif')->nullable();
$table->unsignedInteger('jumlah_anggota_terdaftar')->nullable();
$table->unsignedInteger('jumlah_anggota_aktif')->nullable();
$table->unsignedInteger('jumlah_pengurus_laki_laki')->nullable();
$table->unsignedInteger('jumlah_pengurus_perempuan')->nullable();
$table->unsignedInteger('jumlah_anggota_laki_laki')->nullable();
$table->unsignedInteger('jumlah_anggota_perempuan')->nullable();
$table->unsignedInteger('jumlah_anggota_pemuda_laki_laki')->nullable();
$table->unsignedInteger('jumlah_anggota_pemuda_perempuan')->nullable();
// Cakupan Wilayah Kelompok
$table->string('cakupan_wilayah')->nullable();
$table->text('nama_wilayah_cakupan')->nullable();
// Status Legalitas Kelompok
$table->string('status_legalitas', 50)->nullable();
$table->string('nomor_sk', 100)->nullable();
$table->string('tanggal_tahun_penerbitan_sk', 30)->nullable();
// Kualitas Kepemimpinan
$table->string('tingkat_keaktifan_ketua', 30)->nullable();
$table->string('status_pembagian_peran', 30)->nullable();
$table->string('status_dokumentasi_rapat', 30)->nullable();
// Frekuensi Pertemuan Rutin
$table->string('frekuensi_pertemuan', 50)->nullable();
$table->date('tanggal_pertemuan_terakhir')->nullable();
// Kegiatan Kongkret yang Sudah Berjalan
$table->jsonb('kegiatan_terlaksana')->nullable();
$table->decimal('estimasi_panen_minimal', 10, 2)->nullable();
$table->decimal('estimasi_panen_maksimal', 10, 2)->nullable();
$table->string('satuan_hasil_panen')->nullable();
$table->jsonb('tindakan_hasil_panen')->nullable();
// Peran & Fungsi Kelompok
$table->jsonb('fungsi_kelompok_bagi_anggota')->nullable();
// Kapasitas Pengelolaan Kas/Simpan-Pinjam Kelompok
$table->string('pembukuan_kas', 50)->nullable();
$table->string('pengelola_kas')->nullable();
$table->string('masalah_pengelolaan_kas_simpan_pinjam')->nullable();
$table->text('deskripsi_masalah_pengelolaan_kas_simpan_pinjam')->nullable();
// Keterlibatan Perempuan
$table->string('partisipasi_perempuan')->nullable();
// Riwayat konflik Internal Kelompok
$table->string('konflik_kelompok')->nullable();
$table->string('topik_konflik')->nullable();
$table->text('penyelesaian_konflik')->nullable();
// Pengalaman Akses Bantuan
$table->boolean('pernah_mendapatkan_bantuan')->nullable();
// Postensi Sebagai Mitra Agregasi/Pengolahan Kolektif
$table->string('potensi_kelompok')->nullable();
$table->text('alasan_penilaian_potensi')->nullable();
$table->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null');
$table->foreignId('updated_by')->nullable()->constrained('users')->onDelete('set null');
$table->foreignId('deleted_by')->nullable()->constrained('users')->onDelete('set null');
$table->timestamps();
$table->softDeletes();
});
Schema::create('survey_pengurus_kelompok_tani', function (Blueprint $table) {
$table->id();
$table->string('uid')->unique();
$table->foreignId('survey_kelompok_tani_id')->nullable()->constrained('survey_kelompok_tani')->cascadeOnUpdate()->cascadeOnDelete();
$table->string('nama_pengurus');
$table->string('jabatan');
$table->string('kontak');
$table->boolean('status');
$table->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null');
$table->foreignId('updated_by')->nullable()->constrained('users')->onDelete('set null');
$table->foreignId('deleted_by')->nullable()->constrained('users')->onDelete('set null');
$table->timestamps();
$table->softDeletes();
});
Schema::create('survey_aset_kelompok_tani', function (Blueprint $table) {
$table->id();
$table->string('uid')->unique();
$table->foreignId('survey_kelompok_tani_id')->nullable()->constrained('survey_kelompok_tani')->cascadeOnUpdate()->cascadeOnDelete();
$table->string('jenis_aset');
$table->boolean('status_aset');
$table->string('status_pengelolaan_aset');
$table->string('kondisi_aset');
$table->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null');
$table->foreignId('updated_by')->nullable()->constrained('users')->onDelete('set null');
$table->foreignId('deleted_by')->nullable()->constrained('users')->onDelete('set null');
$table->timestamps();
$table->softDeletes();
});
Schema::create('survey_bantuan_kelompok_tani', function (Blueprint $table) {
$table->id();
$table->string('uid')->unique();
$table->foreignId('survey_kelompok_tani_id')->nullable()->constrained('survey_kelompok_tani')->cascadeOnUpdate()->cascadeOnDelete();
$table->string('nama_program');
$table->string('penyelenggara');
$table->string('bentuk_bantuan');
$table->year('tahun');
$table->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null');
$table->foreignId('updated_by')->nullable()->constrained('users')->onDelete('set null');
$table->foreignId('deleted_by')->nullable()->constrained('users')->onDelete('set null');
$table->timestamps();
$table->softDeletes();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('survey_bantuan_kelompok_tani');
Schema::dropIfExists('survey_aset_kelompok_tani');
Schema::dropIfExists('survey_pengurus_kelompok_tani');
Schema::dropIfExists('survey_kelompok_tani');
}
};
@@ -0,0 +1,144 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('pengurus_koperasi', function (Blueprint $table) {
$table->id();
// --- PROFIL DASAR ---
$table->string('nama_lengkap');
$table->string('posisi_dalam_koperasi');
$table->integer('tahun_bergabung')->nullable();
$table->string('no_hp', 20)->nullable();
$table->text('alamat_tinggal')->nullable();
$table->string('nama_koperasi');
$table->text('alamat_koperasi')->nullable();
$table->string('jenis_koperasi');
$table->string('skala_operasi');
$table->integer('tahun_berdiri')->nullable();
// --- ANGGOTA & PENGURUS ---
$table->integer('jumlah_anggota_awal')->nullable();
$table->integer('jumlah_anggota_dewasa')->nullable();
$table->integer('jumlah_anggota_anak')->nullable();
$table->integer('jumlah_pengurus')->nullable();
// --- SHU ---
$table->bigInteger('shu_2025')->nullable();
$table->bigInteger('shu_2024')->nullable();
$table->bigInteger('shu_2023')->nullable();
// --- KELEMBAGAAN & MANAJEMEN ---
$table->text('sejarah_lembaga')->nullable();
$table->string('fokus_komoditas')->nullable();
$table->string('detail_fokus_komoditas')->nullable();
$table->string('ad_art')->nullable();
$table->integer('tahun_ad_art')->nullable();
$table->text('layanan_koperasi')->nullable();
$table->string('layanan_lainnya')->nullable();
// Pelatihan
$table->string('pelatihan_produksi')->nullable();
$table->string('pelatihan_pascapanen')->nullable();
$table->string('pelatihan_pengolahan')->nullable();
$table->string('pelatihan_keuangan')->nullable();
$table->string('pelatihan_pemasaran')->nullable();
$table->string('pelatihan_lainnya')->nullable();
// Operasional
$table->string('status_pembukuan')->nullable();
$table->string('format_pembukuan')->nullable();
$table->string('pertemuan_rutin')->nullable();
$table->string('pertemuan_lainnya')->nullable();
$table->string('kasus_kredit_macet')->nullable();
$table->string('sanksi_kredit_macet')->nullable();
$table->string('efektivitas_sanksi')->nullable();
$table->text('faktor_manajemen')->nullable();
$table->string('faktor_manajemen_lainnya')->nullable();
// Rantai Nilai & Fasilitas Internal
$table->text('keterlibatan_rantai_nilai')->nullable();
$table->text('agregasi_komoditas')->nullable();
$table->string('agregasi_komoditas_lainnya')->nullable();
$table->text('fasilitas_koperasi')->nullable();
$table->string('fasilitas_lainnya')->nullable();
$table->string('pengolahan_lanjutan')->nullable();
$table->string('detail_pengolahan_lanjutan')->nullable();
$table->text('cara_penentuan_harga')->nullable();
$table->string('kontrak_offtaker')->nullable();
$table->string('kontrak_komoditas')->nullable();
// --- AKSES PASAR & RANTAI NILAI EKSTERNAL ---
$table->text('sumber_pasokan')->nullable();
$table->string('sumber_pasokan_lainnya')->nullable();
$table->text('bentuk_pembelian')->nullable();
$table->integer('volume_beli_sekarang')->nullable();
$table->integer('volume_beli_1_tahun')->nullable();
$table->integer('volume_beli_2_tahun')->nullable();
$table->text('tujuan_penjualan')->nullable();
$table->string('tujuan_penjualan_lainnya')->nullable();
$table->text('bentuk_penjualan')->nullable();
$table->integer('volume_jual_sekarang')->nullable();
$table->integer('volume_jual_1_tahun')->nullable();
$table->integer('volume_jual_2_tahun')->nullable();
$table->text('sifat_hubungan_bisnis')->nullable();
$table->string('sifat_hubungan_lainnya')->nullable();
$table->string('mekanisme_pembayaran')->nullable();
$table->string('status_perjanjian_tertulis')->nullable();
// Harga Komoditas
$table->bigInteger('harga_segar_sekarang')->nullable();
$table->bigInteger('harga_segar_1_tahun')->nullable();
$table->bigInteger('harga_segar_2_tahun')->nullable();
$table->bigInteger('harga_basah_sekarang')->nullable();
$table->bigInteger('harga_basah_1_tahun')->nullable();
$table->bigInteger('harga_basah_2_tahun')->nullable();
$table->bigInteger('harga_kering_sekarang')->nullable();
$table->bigInteger('harga_kering_1_tahun')->nullable();
$table->bigInteger('harga_kering_2_tahun')->nullable();
$table->bigInteger('harga_fermentasi_sekarang')->nullable();
$table->bigInteger('harga_fermentasi_1_tahun')->nullable();
$table->bigInteger('harga_fermentasi_2_tahun')->nullable();
$table->text('cara_menetapkan_harga_pasar')->nullable();
$table->string('cara_menetapkan_harga_lainnya')->nullable();
// Praktik Kualitas & Pascapanen
$table->string('praktik_sortasi')->nullable();
$table->string('standar_sortasi')->nullable();
$table->string('praktik_pengeringan')->nullable();
$table->string('cara_pengeringan')->nullable();
$table->string('praktik_fermentasi')->nullable();
$table->text('model_fermentasi')->nullable();
$table->text('proses_fermentasi')->nullable();
$table->bigInteger('harga_premium_rupiah')->nullable();
$table->integer('harga_premium_persen')->nullable();
// --- SOSIAL EKONOMI ---
$table->bigInteger('simpanan_terbesar')->nullable();
$table->string('momen_simpanan_terbesar')->nullable();
$table->text('alasan_pinjaman')->nullable();
$table->string('alasan_pinjaman_lainnya')->nullable();
$table->string('pembayaran_selain_uang')->nullable();
$table->text('bentuk_pembayaran_barang')->nullable();
$table->string('bentuk_pembayaran_lainnya')->nullable();
$table->string('perhitungan_harga_pasar')->nullable();
$table->string('alasan_bukan_harga_pasar')->nullable();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('pengurus_koperasi');
}
};
@@ -0,0 +1,50 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('bumdes_kelembagaan_aktor', function (Blueprint $table) {
$table->id();
$table->foreignId('pengurus_bumdes_id')->unique()->constrained('bumdes_pengurus')->onDelete('cascade');
$table->string('status_badan_hukum')->nullable();
$table->string('nomor_dasar_hukum')->nullable();
$table->unsignedSmallInteger('tahun_dasar_hukum')->nullable();
$table->string('status_keaktifan')->nullable();
$table->text('alasan_tidak_aktif')->nullable();
$table->json('unit_usaha')->nullable();
$table->string('unit_usaha_jasa_lainnya')->nullable();
$table->string('status_unit_komoditas')->nullable();
$table->string('nama_unit_komoditas')->nullable();
$table->string('status_ad_art')->nullable();
$table->unsignedSmallInteger('tahun_ad_art')->nullable();
$table->json('pengurus_inti')->nullable();
$table->boolean('memiliki_staf')->nullable();
$table->unsignedInteger('jumlah_staf')->nullable();
$table->text('unit_usaha_staf')->nullable();
$table->decimal('modal_awal', 18, 2)->nullable();
$table->unsignedSmallInteger('tahun_modal_awal')->nullable();
$table->boolean('pernah_penambahan_modal')->nullable();
$table->json('penambahan_modal')->nullable();
$table->json('aset_produktif')->nullable();
$table->string('aset_produktif_lainnya')->nullable();
$table->string('status_kepemilikan_aset')->nullable();
$table->json('infrastruktur_pascapanen')->nullable();
$table->string('infrastruktur_pascapanen_lainnya')->nullable();
$table->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null');
$table->foreignId('updated_by')->nullable()->constrained('users')->onDelete('set null');
$table->foreignId('deleted_by')->nullable()->constrained('users')->onDelete('set null');
$table->timestamps();
$table->softDeletes();
});
}
public function down(): void
{
Schema::dropIfExists('bumdes_kelembagaan_aktor');
}
};
@@ -0,0 +1,479 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('riwayat_petani', function (Blueprint $table) {
$table->id();
$table->string('periode_pencatatan')->comment('Penanda waktu snapshot, misal: Q1 2026');
$table->string('uid')->nullable();
$table->foreignId('petani_id')->constrained('petani')->onDelete('cascade');
$table->string('nama');
$table->boolean('jenis_kelamin');
$table->string('tempat_lahir')->nullable();
$table->date('tanggal_lahir');
$table->string('pendidikan_terakhir')->nullable();
$table->string('alamat')->nullable();
$table->foreignId('desa_kelurahan_id')->nullable()->constrained('master_desa_kelurahan');
$table->string('nomor_telepon')->nullable();
$table->string('foto', 500)->nullable();
$table->string('status_perkawinan')->nullable();
$table->string('kk')->nullable();
$table->string('ktp')->nullable();
$table->string('path_kk', 500)->nullable();
$table->string('file_kk')->nullable();
$table->string('path_ktp', 500)->nullable();
$table->string('file_ktp')->nullable();
$table->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null');
$table->foreignId('updated_by')->nullable()->constrained('users')->onDelete('set null');
$table->foreignId('deleted_by')->nullable()->constrained('users')->onDelete('set null');
$table->timestamps();
$table->softDeletes();
});
Schema::create('riwayat_map_lahan', function (Blueprint $table) {
$table->id();
$table->string('periode_pencatatan')->comment('Penanda waktu snapshot, misal: Q1 2026');
$table->string('uid')->nullable();
$table->string('nama');
$table->string('status_kepemilikan');
$table->foreignId('profile_id')->constrained('profiles');
$table->foreignId('keluarga_pengelola_id')->nullable()->constrained('keluarga')->onDelete('set null');
$table->foreignId('adat_pemilik_id')->nullable()->constrained('adat')->onDelete('set null');
$table->foreignId('desa_kelurahan_id')->nullable()->constrained('master_desa_kelurahan');
$table->string('lat')->nullable();
$table->string('long')->nullable();
$table->jsonb('path')->nullable();
$table->boolean('sertifikat')->default(false);
$table->boolean('konflik')->default(false);
$table->text('deskripsi')->nullable();
$table->float('luas_lahan', 8)->nullable();
$table->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null');
$table->foreignId('updated_by')->nullable()->constrained('users')->onDelete('set null');
$table->foreignId('deleted_by')->nullable()->constrained('users')->onDelete('set null');
$table->timestamps();
$table->softDeletes();
});
Schema::create('riwayat_sertifikat_lahan', function (Blueprint $table) {
$table->id();
$table->string('periode_pencatatan');
$table->string('uid')->nullable();
$table->string('nomor_sertifikat');
$table->date('tanggal_terbit');
$table->date('tanggal_kadaluarsa')->nullable();
$table->string('lembaga_penerbit')->nullable();
$table->foreignId('lahan_id')->constrained('map_lahan')->onDelete('cascade');
$table->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null');
$table->foreignId('updated_by')->nullable()->constrained('users')->onDelete('set null');
$table->foreignId('deleted_by')->nullable()->constrained('users')->onDelete('set null');
$table->timestamps();
$table->softDeletes();
});
Schema::create('riwayat_map_tanaman', function (Blueprint $table) {
$table->id();
$table->string('periode_pencatatan');
$table->string('uid')->nullable();
$table->string('nama');
$table->foreignId('komoditas_id')->constrained('master_komoditas')->onDelete('set null');
$table->foreignId('jenis_komoditas_id')->constrained('jenis_komoditas')->onDelete('set null');
$table->foreignId('varietas_id')->constrained('master_varietas')->onDelete('set null');
$table->foreignId('profile_id')->constrained('profiles')->onDelete('set null');
$table->foreignId('lahan_id')->constrained('map_lahan')->onDelete('set null');
$table->float('latitude')->nullable();
$table->float('longitude')->nullable();
$table->string('kondisi_tanaman')->nullable();
$table->string('metode_pengumpulan')->nullable();
$table->text('deskripsi')->nullable();
$table->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null');
$table->foreignId('updated_by')->nullable()->constrained('users')->onDelete('set null');
$table->foreignId('deleted_by')->nullable()->constrained('users')->onDelete('set null');
$table->timestamps();
$table->softDeletes();
});
Schema::create('riwayat_praktik_budidaya', function (Blueprint $table) {
$table->id();
$table->string('periode_pencatatan');
$table->string('uid')->nullable();
$table->foreignID('petani_id')->constrained('petani')->onDelete('cascade');
$table->string('tenaga_kerja')->nullable();
$table->integer('frekuensi_pemangkasan_utama')->nullable();
$table->jsonb('teknik_pemangkasan')->nullable();
$table->string('frekuensi_pemangkasan_penaung')->nullable();
$table->string('penyerbukan_manual')->nullable();
$table->string('deskripsi_penyerbukan_manual')->nullable();
$table->string('tingkat_keberhasilan_penyerbukan')->nullable();
$table->integer('pemberian_pupuk')->nullable();
$table->string('jenis_pupuk')->nullable();
$table->string('frekuensi_pemberian_pupuk')->nullable();
$table->decimal('estimasi_dosis_pupuk_organik', 10, 2)->nullable();
$table->decimal('estimasi_dosis_pupuk_anorganik', 10, 2)->nullable();
$table->string('waktu_pemberian_pupuk')->nullable();
$table->string('cara_pemberian_pupuk')->nullable();
$table->integer('metode_penanganan_opt')->nullable();
$table->jsonb('jenis_hama')->nullable();
$table->jsonb('jenis_penyakit')->nullable();
$table->string('intensitas_serangan')->nullable();
$table->string('waktu_kemunculan_terparah')->nullable();
$table->string('metode_paling_sering_dilakukan')->nullable();
$table->decimal('volume_obat_obatan_padat_permusim', 10, 2)->nullable();
$table->decimal('volume_obat_obatan_cair_permusim', 10, 2)->nullable();
$table->integer('frekuensi_sanitasi_lahan')->nullable();
$table->string('penanganan_sisa_buah_ranting')->nullable();
$table->integer('penanganan_gulma')->nullable();
$table->integer('kondisi_pengelolaan_air')->nullable();
$table->jsonb('sumber_air')->nullable();
$table->string('sistem_irigasi')->nullable();
$table->string('kebutuhan_air_permusim')->nullable();
$table->jsonb('kebiasaan_konservasi_tanah')->nullable();
$table->string('pemahaman_manfaat_konservasi_tanah')->nullable();
$table->integer('nilai_konservasi_tanah')->nullable();
$table->decimal('upah_tenaga_kerja', 13, 2)->nullable();
$table->decimal('biaya_bibit', 13, 2)->nullable();
$table->decimal('biaya_pupuk', 13, 2)->nullable();
$table->decimal('biaya_obat_obatan', 13, 2)->nullable();
$table->decimal('biaya_sewa_alat_produksi', 13, 2)->nullable();
$table->decimal('biaya_penyediaan_air', 13, 2)->nullable();
$table->decimal('biaya_produksi_lainnya', 13, 2)->nullable();
$table->decimal('total_estimasi_biaya_produksi', 13, 2)->nullable();
$table->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null');
$table->foreignId('updated_by')->nullable()->constrained('users')->onDelete('set null');
$table->foreignId('deleted_by')->nullable()->constrained('users')->onDelete('set null');
$table->timestamps();
$table->softDeletes();
});
Schema::create('riwayat_survey_panen', function (Blueprint $table) {
$table->id();
$table->string('periode_pencatatan');
$table->string('uid')->nullable();
$table->foreignId('petani_id')->nullable()->constrained('petani')->onDelete('set null');
$table->integer('dasar_penentuan_panen')->nullable();
$table->integer('frekuensi_panen_pertahun')->nullable();
$table->string('bulan_panen_utama')->nullable();
$table->decimal('hasil_komoditas_utama_permusim', 10, 2)->nullable();
$table->decimal('hasil_komoditas_lainnya_permusim', 10, 2)->nullable();
$table->boolean('disimpan_untuk_sendiri')->nullable();
$table->decimal('hasil_panen_disimpan', 10, 2)->nullable();
$table->string('tujuan_disimpan')->nullable();
$table->integer('praktik_sortasi')->nullable();
$table->string('bentuk_sortasi')->nullable();
$table->integer('hasil_panen_reject')->nullable();
$table->string('jumlah_panen_reject')->nullable();
$table->string('tindakan_panen_reject')->nullable();
$table->decimal('upah_tenaga_panen', 13, 2)->nullable();
$table->decimal('biaya_pengangkutan_panen', 13, 2)->nullable();
$table->decimal('biaya_wadah_Panen', 13, 2)->nullable();
$table->decimal('biaya_sewa_alat_panen', 13, 2)->nullable();
$table->decimal('biaya_panen_lainnya', 13, 2)->nullable();
$table->decimal('total_estimasi_biaya_panen', 13, 2)->nullable();
$table->string('estimasi_kehilangan_akibat_opt')->nullable();
$table->string('estimasi_kehilangan_akibat_hama_penyakit')->nullable();
$table->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null');
$table->foreignId('updated_by')->nullable()->constrained('users')->onDelete('set null');
$table->foreignId('deleted_by')->nullable()->constrained('users')->onDelete('set null');
$table->timestamps();
$table->softDeletes();
});
Schema::create('riwayat_survey_produktivitas', function (Blueprint $table) {
$table->id();
$table->string('periode_pencatatan');
$table->string('uid')->nullable();
$table->foreignId('petani_id')->nullable()->constrained('petani')->onDelete('set null');
$table->foreignId('komoditas_id')->nullable()->constrained('master_komoditas')->onDelete('set null');
$table->decimal('kuantitas')->nullable();
$table->string('satuan')->nullable();
$table->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null');
$table->foreignId('updated_by')->nullable()->constrained('users')->onDelete('set null');
$table->foreignId('deleted_by')->nullable()->constrained('users')->onDelete('set null');
$table->timestamps();
$table->softDeletes();
});
Schema::create('riwayat_sosial_ekonomi', function (Blueprint $table) {
$table->id();
$table->string('periode_pencatatan');
$table->string('uid')->nullable();
$table->foreignId('petani_id')->nullable()->constrained('petani')->onDelete('cascade');
$table->integer('jumlah_tanggungan')->nullable();
$table->integer('anak_masih_sekolah')->nullable();
$table->integer('yang_ikut_bekerja')->nullable();
$table->tinyInteger('skor_periode_paceklik')->nullable();
$table->string('bulan_paceklik')->nullable();
$table->text('sumber_penghasilan_paceklik')->nullable();
$table->unsignedBigInteger('total_estimasi_biaya_kebun_per_musim')->nullable();
$table->tinyInteger('skor_kemampuan_menabung')->nullable();
$table->unsignedBigInteger('estimasi_tabungan_per_bulan')->nullable();
$table->tinyInteger('skor_kecukupan_penghasilan')->nullable();
$table->text('alasan_kecukupan_penghasilan')->nullable();
$table->string('status_kepemilikan_lahan')->nullable();
$table->tinyInteger('skor_kejelasan_status_lahan')->nullable();
$table->boolean('ada_sertifikat_dokumen')->nullable();
$table->boolean('ada_sengketa')->nullable();
$table->text('keterangan_sengketa')->nullable();
$table->string('pihak_membantu_konflik')->nullable();
$table->string('pihak_membantu_konflik_nama')->nullable();
$table->year('tahun_bantuan_konflik')->nullable();
$table->string('status_konflik')->nullable();
$table->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null');
$table->foreignId('updated_by')->nullable()->constrained('users')->onDelete('set null');
$table->foreignId('deleted_by')->nullable()->constrained('users')->onDelete('set null');
$table->timestamps();
$table->softDeletes();
});
Schema::create('riwayat_pendapatan_komoditas', function (Blueprint $table) {
$table->id();
$table->string('periode_pencatatan');
$table->string('uid')->nullable();
$table->foreignId('sosial_ekonomi_id')->constrained('sosial_ekonomi')->onDelete('cascade');
$table->string('kategori')->comment('utama / lainnya');
$table->string('komoditas')->nullable();
$table->unsignedBigInteger('pendapatan')->nullable();
$table->string('periode')->nullable()->comment('musim / tahun');
$table->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null');
$table->foreignId('updated_by')->nullable()->constrained('users')->onDelete('set null');
$table->foreignId('deleted_by')->nullable()->constrained('users')->onDelete('set null');
$table->timestamps();
});
Schema::create('riwayat_pendapatan_luar_pertanian', function (Blueprint $table) {
$table->id();
$table->string('periode_pencatatan');
$table->string('uid')->nullable();
$table->foreignId('sosial_ekonomi_id')->constrained('sosial_ekonomi')->onDelete('cascade');
$table->string('jenis_pekerjaan')->nullable();
$table->unsignedBigInteger('estimasi_per_bulan')->nullable();
$table->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null');
$table->foreignId('updated_by')->nullable()->constrained('users')->onDelete('set null');
$table->foreignId('deleted_by')->nullable()->constrained('users')->onDelete('set null');
$table->timestamps();
});
Schema::create('riwayat_akses_pasar', function (Blueprint $table) {
$table->id();
$table->string('periode_pencatatan');
$table->string('uid')->nullable();
$table->foreignId('petani_id')->nullable()->constrained('petani')->onDelete('cascade');
$table->tinyInteger('skor_pengalaman')->nullable();
$table->json('saluran_terdahulu')->nullable();
$table->string('saluran_terdahulu_lainnya')->nullable();
$table->string('alasan_berhenti')->nullable();
$table->string('praktik_fermentasi')->nullable();
$table->text('proses_fermentasi')->nullable();
$table->string('model_fermentasi_memungkinkan')->nullable();
$table->tinyInteger('skor_kesadaran_premium')->nullable();
$table->string('sumber_info_premium')->nullable();
$table->time('waktu_mulai_wawancara')->nullable();
$table->time('waktu_selesai_wawancara')->nullable();
$table->tinyInteger('skor_kepercayaan_responden')->nullable();
$table->string('kehadiran_orang_lain')->nullable();
$table->tinyInteger('skor_pengaruh_orang_lain')->nullable();
$table->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null');
$table->foreignId('updated_by')->nullable()->constrained('users')->onDelete('set null');
$table->foreignId('deleted_by')->nullable()->constrained('users')->onDelete('set null');
$table->timestamps();
$table->softDeletes();
});
Schema::create('riwayat_harga_jual_panen', function (Blueprint $table) {
$table->id();
$table->string('periode_pencatatan');
$table->string('uid')->nullable();
$table->foreignId('petani_id')->constrained('petani')->onDelete('cascade');
$table->string('bentuk_produk');
$table->integer('harga_panen_terakhir')->nullable();
$table->integer('harga_tahun_lalu')->nullable();
$table->integer('harga_dua_tahun_lalu')->nullable();
$table->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null');
$table->foreignId('updated_by')->nullable()->constrained('users')->onDelete('set null');
$table->foreignId('deleted_by')->nullable()->constrained('users')->onDelete('set null');
$table->timestamps();
});
Schema::create('riwayat_survey_pelatihan', function (Blueprint $table) {
$table->id();
$table->string('periode_pencatatan');
$table->string('uid')->nullable();
$table->foreignId('petani_id')->nullable()->constrained('petani')->onDelete('set null');
$table->string('jenis_pelatihan');
$table->string('penyelenggara');
$table->integer('tahun_pelaksanaan');
$table->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null');
$table->foreignId('updated_by')->nullable()->constrained('users')->onDelete('set null');
$table->foreignId('deleted_by')->nullable()->constrained('users')->onDelete('set null');
$table->timestamps();
$table->softDeletes();
});
Schema::create('riwayat_survey_sertifikasi', function (Blueprint $table) {
$table->id();
$table->string('periode_pencatatan');
$table->string('uid')->nullable();
$table->foreignId('petani_id')->nullable()->constrained('petani')->onDelete('set null');
$table->string('jenis_sertifikasi');
$table->string('lembaga_penerbit');
$table->integer('tahun_pelaksanaan');
$table->boolean('is_aktif')->default(false);
$table->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null');
$table->foreignId('updated_by')->nullable()->constrained('users')->onDelete('set null');
$table->foreignId('deleted_by')->nullable()->constrained('users')->onDelete('set null');
$table->timestamps();
$table->softDeletes();
});
Schema::create('riwayat_petani_skor', function (Blueprint $table) {
$table->id();
$table->string('periode_pencatatan');
$table->string('uid')->nullable(); // Unique dihilangkan
$table->foreignId('petani_id')->nullable()->constrained('petani')->onDelete('cascade');
$table->float('skor_kesiapan_adopsi', 10)->nullable();
$table->float('skor_d2_sistem_praktik_budidaya', 10)->nullable();
$table->float('skor_d4_sosial_ekonomi', 10)->nullable();
$table->float('skor_d6_rantai_nilai_akses_pasar', 10)->nullable();
$table->float('skor_komp_1_d4_k3_motivasi_kapasitas_adopsi', 10)->nullable();
$table->float('skor_komp_2_d4_k2_ekonomi_rumah_tangga', 10)->nullable();
$table->float('skor_komp_3_d2_praktik_budidaya_gap', 10)->nullable();
$table->float('skor_komp_4_d4_k4_keterlibatan_kelembagaan', 10)->nullable();
$table->float('skor_komp_5_d4_k5_k6_akses_kebebasan_keuangan', 10)->nullable();
$table->float('skor_komp_6_d6_k1_k4_k5_rantai_nilai_akses_pasar', 10)->nullable();
$table->float('skor_d4_k3_1_praktik_baru', 10)->nullable();
$table->float('skor_d4_k3_4_pemahaman_mutu_harga', 10)->nullable();
$table->float('skor_d4_k3_5_role_model', 10)->nullable();
$table->float('skor_d4_k3_6_kemampuan_keterlibatan_program', 10)->nullable();
$table->float('skor_d4_k3_7_orientasi_jangka_panjang', 10)->nullable();
$table->float('skor_d4_k2_5_stabilitas_penghasilan', 10)->nullable();
$table->float('skor_d4_k2_8_kemampuan_menabung', 10)->nullable();
$table->float('skor_d4_k2_9_kecukupan_penghasilan', 10)->nullable();
$table->float('skor_d4_k2_3_k2_7_rasio_biaya_produksi_pendapatan', 10)->nullable();
$table->float('skor_d4_k2_11_hak_kelola_lahan', 10)->nullable();
$table->float('skor_d2_k2_3_praktik_pemangkasan', 10)->nullable();
$table->float('skor_d2_k2_4_praktik_pemupukan', 10)->nullable();
$table->float('skor_d2_k2_5_pengendalian_opt', 10)->nullable();
$table->float('skor_d2_k2_6_sanitasi_kebun', 10)->nullable();
$table->float('skor_d2_k2_6b_pengelolaan_gulma', 10)->nullable();
$table->float('skor_d2_k2_7_penglolaan_air_irigasi', 10)->nullable();
$table->float('skor_d2_k2_8_praktik_konservasi_tanah', 10)->nullable();
$table->float('skor_d2_k3_1_penentuan_pematangan', 10)->nullable();
$table->float('skor_d2_k3_5_sortasi_awal_pemisahan_kualitas', 10)->nullable();
$table->float('skor_d4_k4_2_keaktifan_pertemuan', 10)->nullable();
$table->float('skor_d4_k4_3_peran_dalam_kelompok', 10)->nullable();
$table->float('skor_d4_k4_4_manfaat_dirasakan', 10)->nullable();
$table->float('skor_d4_k5_1_kepemilikan_rekening', 10)->nullable();
$table->float('skor_d4_k5_2_pengalaman_kredit_formal', 10)->nullable();
$table->float('skor_d4_k5_3_kepemilikan_aset_produktif', 10)->nullable();
$table->float('skor_d4_k5_4_akses_program_bantuan', 10)->nullable();
$table->float('skor_d4_k6_3_keberadaan_hutang_terkait_panen', 10)->nullable();
$table->float('skor_d4_k6_5a_praktik_ijon', 10)->nullable();
$table->float('skor_d4_k6_5b_kebebasan_memilih_pembeli', 10)->nullable();
$table->float('skor_d6_k1_6_pengalaman_menjual_ke_saluran_berbeda', 10)->nullable();
$table->float('skor_d6_k4_1_kesadaran_akses_pasar_premium', 10)->nullable();
$table->float('skor_d6_k5_1_hambatan_mutu', 10)->nullable();
$table->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null');
$table->foreignId('updated_by')->nullable()->constrained('users')->onDelete('set null');
$table->foreignId('deleted_by')->nullable()->constrained('users')->onDelete('set null');
$table->timestamps();
$table->softDeletes();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('riwayat_petani_skor');
Schema::dropIfExists('riwayat_survey_sertifikasi');
Schema::dropIfExists('riwayat_survey_pelatihan');
Schema::dropIfExists('riwayat_harga_jual_panen');
Schema::dropIfExists('riwayat_akses_pasar');
Schema::dropIfExists('riwayat_pendapatan_luar_pertanian');
Schema::dropIfExists('riwayat_pendapatan_komoditas');
Schema::dropIfExists('riwayat_sosial_ekonomi');
Schema::dropIfExists('riwayat_survey_produktivitas');
Schema::dropIfExists('riwayat_survey_panen');
Schema::dropIfExists('riwayat_praktik_budidaya');
Schema::dropIfExists('riwayat_map_tanaman');
Schema::dropIfExists('riwayat_sertifikat_lahan');
Schema::dropIfExists('riwayat_map_lahan');
}
};
@@ -0,0 +1,38 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('akses_pasar', function (Blueprint $table) {
$table->text('tujuan_penjualan')->nullable();
$table->string('tujuan_penjualan_lainnya')->nullable();
$table->text('sifat_hubungan_bisnis')->nullable();
$table->text('bentuk_penjualan')->nullable();
$table->string('frekuensi_penjualan')->nullable();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('akses_pasar', function (Blueprint $table) {
$table->dropColumn([
'tujuan_penjualan',
'tujuan_penjualan_lainnya',
'sifat_hubungan_bisnis',
'bentuk_penjualan',
'frekuensi_penjualan'
]);
});
}
};
@@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('akses_pasar', function (Blueprint $table) {
$table->string('tahu_harga_sebelumnya')->nullable();
$table->text('sumber_info_harga')->nullable();
$table->string('sumber_info_harga_lainnya')->nullable();
});
}
public function down(): void
{
Schema::table('akses_pasar', function (Blueprint $table) {
$table->dropColumn([
'tahu_harga_sebelumnya',
'sumber_info_harga',
'sumber_info_harga_lainnya',
]);
});
}
};
+151
View File
@@ -103,3 +103,154 @@
text-align: center;
margin-top: 1rem;
}
/* Sidebar icon tiles: keep the icon mask on the inner element. */
#layout-menu .menu-inner > .menu-item > .menu-link > .sidebar-icon {
display: inline-flex;
align-items: center;
justify-content: center;
flex: 0 0 30px;
width: 30px;
height: 30px;
border-radius: 9px;
color: var(--sidebar-icon-color, var(--bs-secondary));
background: var(--sidebar-icon-bg, rgba(var(--bs-secondary-rgb), 0.1));
transition: background-color 180ms ease, color 180ms ease, transform 180ms ease;
}
#layout-menu .sidebar-icon > .ti {
width: 21px;
height: 21px;
font-size: 21px;
}
#layout-menu .sidebar-icon:has(.tabler-dashboard),
#layout-menu .sidebar-icon:has(.tabler-database) {
--sidebar-icon-color: var(--bs-primary);
--sidebar-icon-bg: rgba(var(--bs-primary-rgb), 0.12);
}
#layout-menu .sidebar-icon:has(.tabler-user-circle),
#layout-menu .sidebar-icon:has(.tabler-id-badge-2),
#layout-menu .sidebar-icon:has(.tabler-plant) {
--sidebar-icon-color: var(--bs-success);
--sidebar-icon-bg: rgba(var(--bs-success-rgb), 0.12);
}
#layout-menu .sidebar-icon:has(.tabler-users-group),
#layout-menu .sidebar-icon:has(.tabler-sitemap) {
--sidebar-icon-color: var(--bs-info);
--sidebar-icon-bg: rgba(var(--bs-info-rgb), 0.12);
}
#layout-menu .sidebar-icon:has(.tabler-building-community),
#layout-menu .sidebar-icon:has(.tabler-heart-handshake) {
--sidebar-icon-color: var(--bs-warning);
--sidebar-icon-bg: rgba(var(--bs-warning-rgb), 0.12);
}
#layout-menu .sidebar-icon:has(.tabler-map-2),
#layout-menu .sidebar-icon:has(.tabler-user-shield),
#layout-menu .sidebar-icon:has(.tabler-route) {
--sidebar-icon-color: var(--bs-primary);
--sidebar-icon-bg: rgba(var(--bs-primary-rgb), 0.12);
}
#layout-menu .menu-item > .menu-link:hover > .sidebar-icon,
#layout-menu .menu-item > .menu-link:focus-visible > .sidebar-icon {
transform: translateY(-1px);
}
#layout-menu .menu-inner > .menu-item.active > .menu-link > .sidebar-icon {
color: inherit;
background: rgba(255, 255, 255, 0.18);
}
@media (prefers-reduced-motion: reduce) {
#layout-menu .menu-inner > .menu-item > .menu-link > .sidebar-icon {
transition: none;
transform: none;
}
}
/* Submenu icons replace the template's decorative bullets. */
#layout-menu .menu-sub > .menu-item > .menu-link::before { display: none; }
#layout-menu .menu-sub .sidebar-icon {
display: inline-flex;
align-items: center;
justify-content: center;
flex: 0 0 22px;
width: 22px;
height: 22px;
color: inherit;
}
#layout-menu .menu-sub .sidebar-icon > .ti { width: 19px; height: 19px; font-size: 19px; }
/* Profil Kelompok Tani */
.kelompok-tani-page {
font-family: var(--bs-body-font-family);
font-size: var(--bs-body-font-size);
font-weight: var(--bs-body-font-weight);
line-height: var(--bs-body-line-height);
}
.kelompok-tani-page :is(.form-control, .form-select, .form-check-label, .select2-container) {
font-family: var(--bs-body-font-family);
}
.kelompok-tani-page select.is-invalid + .select2-container .select2-selection {
border-color: var(--bs-form-invalid-border-color);
}
.kelompok-tani-page .kelompok-tani-summary-name {
font-size: .9rem;
}
.kelompok-tani-page .kelompok-tani-summary-note {
font-size: .75rem;
line-height: 1.3;
}
.kelompok-tani-page .kelompok-tani-help {
margin-top: -.75rem;
}
.kelompok-tani-page #section-kas[hidden],
.kelompok-tani-page #section-bantuan[hidden] {
display: none !important;
}
.kelompok-tani-page #kelompok-tani-notifications,
.kelompok-tani-page [id^="section-"] {
scroll-margin-top: 7rem;
}
.kelompok-tani-page .select2-selection {
position: relative;
}
.kelompok-tani-page .select2-selection::after {
content: '';
position: absolute;
top: 50%;
inset-inline-end: 1rem;
width: .5rem;
height: .5rem;
border-right: 2px solid currentColor;
border-bottom: 2px solid currentColor;
color: var(--bs-body-color);
transform: translateY(-75%) rotate(45deg);
pointer-events: none;
}
.kelompok-tani-page .select2-selection__arrow {
display: none;
}
.kelompok-tani-page .select2-container .select2-selection--multiple .select2-selection__rendered {
padding-inline-end: 2.5rem;
}
.kelompok-tani-page .select2-container--disabled .select2-selection::after {
color: var(--bs-secondary-color);
}
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 421 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 328 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 359 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

+66 -3
View File
@@ -37,10 +37,73 @@ window.config = {
danger: window.Helpers.getCssVar('danger-bg-subtle'),
dark: window.Helpers.getCssVar('dark-bg-subtle')
},
chartColors: {
series: ['#9D8EF0', '#6FD68F', '#FFB46F', '#5FC7D6', '#F47C86', '#9EA6B0', '#65D8BD', '#63C7E0'],
semantic: {
primary: '#9D8EF0',
secondary: '#9EA6B0',
success: '#6FD68F',
info: '#5FC7D6',
warning: '#FFB46F',
danger: '#F47C86',
purple: '#9D8EF0',
teal: '#65D8BD',
pink: '#F47C86',
gray: '#9EA6B0'
},
text: {
primary: '#7367F0',
secondary: '#808390',
success: '#28C76F',
info: '#00BAD1',
warning: '#FF9F43',
danger: '#FF4C51',
purple: '#7367F0',
teal: '#20C997',
pink: '#FF4C51',
gray: '#808390'
},
categories: {
coffee: '#6FD68F',
vanilla: '#FFB46F',
cacao: '#9D8EF0',
candlenut: '#9EA6B0',
shade: '#65D8BD',
policyPurple: '#9D8EF0',
policyPink: '#F47C86',
marketGreen: '#6FD68F',
marketRed: '#F47C86',
marketBlue: '#5FC7D6'
},
grid: '#ECF0F6'
},
fontFamily: window.Helpers.getCssVar('font-family-base'),
enableMenuLocalStorage: true // Enable menu state with local storage support
};
// Choose the label color with the higher contrast against each chart slice.
window.config.chartTextColor = function (color) {
let hex = String(color || '').trim().replace(/^#/, '');
if (/^[0-9a-f]{3}$/i.test(hex)) {
hex = hex.split('').map(value => value + value).join('');
}
if (!/^[0-9a-f]{6}$/i.test(hex)) return '#212529';
const luminance = function (value) {
const channels = value.match(/../g).map(channel => {
const srgb = parseInt(channel, 16) / 255;
return srgb <= 0.04045 ? srgb / 12.92 : Math.pow((srgb + 0.055) / 1.055, 2.4);
});
return channels[0] * 0.2126 + channels[1] * 0.7152 + channels[2] * 0.0722;
};
const background = luminance(hex);
const dark = luminance('212529');
const darkContrast = (Math.max(background, dark) + 0.05) / (Math.min(background, dark) + 0.05);
const lightContrast = 1.05 / (background + 0.05);
return darkContrast >= lightContrast ? '#212529' : '#ffffff';
};
window.assetsPath = document.documentElement.getAttribute('data-assets-path');
window.templateName = document.documentElement.getAttribute('data-template');
@@ -71,13 +134,13 @@ window.templateName = document.documentElement.getAttribute('data-template');
if (typeof TemplateCustomizer !== 'undefined') {
window.templateCustomizer = new TemplateCustomizer({
displayCustomizer: true,
displayCustomizer: false,
lang: localStorage.getItem('templateCustomizer-' + templateName + '--Lang') || 'en', // Set default language here
// defaultPrimaryColor: '#D11BB4',
defaultPrimaryColor: '#2a7233',
// defaultSkin: 1,
// defaultTheme: 'system',
// defaultSemiDark: true,
// defaultContentLayout: 'wide',
defaultContentLayout: 'wide',
// defaultHeaderType: 'static',
// defaultMenuCollapsed: true,
// defaultNavbarType: 'static',
+24 -29
View File
@@ -9,6 +9,9 @@
cardColor = config.colors.cardColor;
labelColor = config.colors.textMuted;
headingColor = config.colors.headingColor;
const pastelChart = config.chartColors || {};
const pastelSemantic = pastelChart.semantic || {};
const pastelSeries = pastelChart.series || ['#9D8EF0', '#6FD68F', '#FFB46F', '#5FC7D6', '#F47C86', '#9EA6B0', '#65D8BD', '#63C7E0'];
// swiper loop and autoplay
// --------------------------------------------------------------------
@@ -48,16 +51,10 @@
grid: {
show: false
},
colors: [config.colors.success],
colors: [pastelSemantic.success || '#6FD68F'],
fill: {
type: 'gradient',
gradient: {
shadeIntensity: 1,
opacityFrom: 0.4,
gradientToColors: [config.colors.cardColor],
opacityTo: 0.1,
stops: [0, 100]
}
type: 'solid',
opacity: 0.22
},
dataLabels: {
enabled: false
@@ -151,13 +148,13 @@
}
},
colors: [
config.colors_label.primary,
config.colors_label.primary,
config.colors_label.primary,
config.colors_label.primary,
config.colors.primary,
config.colors_label.primary,
config.colors_label.primary
pastelSemantic.secondary || '#9EA6B0',
pastelSemantic.secondary || '#9EA6B0',
pastelSemantic.secondary || '#9EA6B0',
pastelSemantic.secondary || '#9EA6B0',
pastelSemantic.primary || '#9D8EF0',
pastelSemantic.secondary || '#9EA6B0',
pastelSemantic.secondary || '#9EA6B0'
],
dataLabels: {
enabled: false
@@ -262,18 +259,10 @@
}
}
},
colors: [config.colors.primary],
colors: [pastelSemantic.primary || '#9D8EF0'],
fill: {
type: 'gradient',
gradient: {
shade: 'dark',
shadeIntensity: 0.5,
gradientToColors: [config.colors.primary],
inverseColors: true,
opacityFrom: 1,
opacityTo: 0.6,
stops: [30, 70, 100]
}
type: 'solid',
opacity: 0.9
},
stroke: {
dashArray: 10
@@ -356,7 +345,10 @@
}
},
colors: [config.colors.primary, config.colors.secondary],
colors: [
pastelSeries[0] || '#9D8EF0',
pastelSeries[1] || '#6FD68F'
],
dataLabels: {
enabled: false
},
@@ -369,7 +361,10 @@
legend: {
show: false
},
colors: [config.colors.primary, config.colors.secondary],
colors: [
pastelSeries[0] || '#9D8EF0',
pastelSeries[1] || '#6FD68F'
],
fill: {
opacity: 1
},
+17 -2
View File
@@ -14,7 +14,7 @@
name="viewport"
content="width=device-width, initial-scale=1.0, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0" />
<meta name="robots" content="noindex, nofollow" />
<title>Agomonev | {{ $title ?? 'Auth' }} </title>
<title>AGERA | {{ $title ?? 'Auth' }} </title>
<meta name="description" content="" />
@@ -26,7 +26,7 @@
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<!-- Favicon -->
<link rel="icon" type="image/x-icon" href="../../assets/img/favicon/favicon.ico" />
<link rel="icon" type="image/svg+xml" href="{{ asset('assets/img/logo/agera_icon.svg') }}" />
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
@@ -72,6 +72,21 @@
<!--? Config: Mandatory theme config file contain global vars & default theme options, Set your preferred theme option in this file. -->
<script src="{{ asset('assets/js/config.js') }}"></script>
<style>
.agera-logo {
width: 200px;
height: auto;
max-height: 78px;
object-fit: contain;
display: block;
}
.app-brand-logo.demo {
width: auto;
height: auto;
}
</style>
</head>
<body>
+2 -14
View File
@@ -6,24 +6,12 @@
<div class="app-brand justify-content-center mb-6">
<a href="{{ route('home') }}" class="app-brand-link">
<span class="app-brand-logo demo">
<span class="text-primary">
<svg width="32" height="24" viewBox="0 0 32 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<!-- Daun Bagian Kiri -->
<path opacity="0.3" d="M16 2.5C16 2.5 5 6.5 5 15.5C5 20.5 11 23.5 16 23.5V2.5Z" fill="currentColor" />
<!-- Daun Bagian Kanan -->
<path opacity="0.6" d="M16 2.5C16 2.5 27 6.5 27 15.5C27 20.5 21 23.5 16 23.5V2.5Z" fill="currentColor" />
<!-- Grafik Pertumbuhan (Monev) -->
<path d="M2 19L10 11L15 15L27 4" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" />
<!-- Titik Target/Pencapaian -->
<circle cx="27" cy="4" r="2.5" fill="currentColor" />
</svg>
</span>
<img src="{{ asset('assets/img/logo/agera_side.svg') }}" alt="AGERA" class="agera-logo">
</span>
<span class="app-brand-text demo menu-text fw-bold ms-3" style="text-transform: capitalize;">AgroMonev</span>
</a>
</div>
<!-- /Logo -->
<h4 class="mb-1">Selamat datang di AgroMonev!</h4>
<h4 class="mb-1">Selamat datang di AGERA!</h4>
<p class="mb-6">Silakan masuk ke akun Anda</p>
@if ($errors->any())
<div class="alert alert-danger">
+2 -30
View File
@@ -6,40 +6,12 @@
<div class="app-brand justify-content-center mb-6">
<a href="{{ route('home') }}" class="app-brand-link">
<span class="app-brand-logo demo">
<span class="text-primary">
<svg width="32" height="24" viewBox="0 0 32 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.3"
d="M16 2.5C16 2.5 5 6.5 5 15.5C5 20.5 11 23.5 16 23.5V2.5Z"
fill="#2E7D32" />
<path opacity="0.6"
d="M16 2.5C16 2.5 27 6.5 27 15.5C27 20.5 21 23.5 16 23.5V2.5Z"
fill="#2E7D32" />
<path d="M2 19L10 11L15 15"
stroke="#0054A6"
stroke-width="2.5"
stroke-linecap="round"
stroke-linejoin="round" />
<path d="M15 15L27 4"
stroke="#ED1C24"
stroke-width="2.5"
stroke-linecap="round"
stroke-linejoin="round" />
<circle cx="27" cy="4" r="2.5" fill="#ED1C24" />
</svg>
</span>
</span>
<span class="app-brand-text demo menu-text fw-bold ms-3" style="text-transform: capitalize;">
AgroMonev
<img src="{{ asset('assets/img/logo/agera_side.svg') }}" alt="AGERA" class="agera-logo">
</span>
</a>
</div>
<!-- /Logo -->
<h4 class="mb-1">Buat akun AgroMonev</h4>
<h4 class="mb-1">Buat akun AGERA</h4>
<p class="mb-6">Lengkapi data berikut untuk membuat akun baru.</p>
<form id="formAuthentication" class="mb-6" action="{{ route('auth.createAccount') }}" method="POST">
@@ -1,7 +1,530 @@
@extends('layout.main')
@php
$fieldValue = function (string $field, $default = null) use ($kelembagaanAktor) {
return old($field, data_get($kelembagaanAktor, $field, $default));
};
$arrayValue = function (string $field) use ($fieldValue) {
return (array) $fieldValue($field, []);
};
$rowValue = function (string $field, string $keyName, string $key) use ($fieldValue) {
return collect((array) $fieldValue($field, []))->firstWhere($keyName, $key) ?? [];
};
$rupiahValue = function ($value) {
if (! filled($value)) {
return '';
}
if (is_numeric($value)) {
return number_format((float) $value, 0, ',', '.');
}
return preg_replace('/\D/', '', (string) $value);
};
@endphp
@section('content')
<div class="container-xxl flex-grow-1 container-p-y">
@if (session('success'))
<div class="alert alert-success">{{ session('success') }}</div>
@endif
@if ($errors->any())
<div class="alert alert-danger">
<ul class="mb-0">
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
@include('bumdes.profil.header', ['pengurus' => $pengurus])
<form method="POST" action="{{ route('bumdes.kelembagaan-aktor.store', $pengurus->id) }}" id="kelembagaanAktorForm">
@csrf
<div class="card mb-6">
<div class="card-header border-bottom">
<h5 class="card-title mb-0">Status Keberadaan, Keaktifan, Kepengurusan dan Modal BUMDes</h5>
</div>
<div class="card-body pt-6">
<h5 class="pb-4 border-bottom mb-4">Status Keberadaan dan Keaktifan</h5>
<div class="mb-5 form-control-validation">
<label class="form-label d-block fw-semibold">Apakah BUMDes sudah berbadan hukum?</label>
<div class="d-flex flex-column">
@foreach($statusBadanHukumOptions as $value => $label)
<div>
<div class="form-check">
<input class="form-check-input" type="radio" id="status-badan-hukum-{{ $value }}" name="status_badan_hukum" value="{{ $value }}" @checked($fieldValue('status_badan_hukum') === $value)>
<label class="form-check-label" for="status-badan-hukum-{{ $value }}">{{ $label }}</label>
</div>
</div>
@endforeach
</div>
@error('status_badan_hukum')<div class="invalid-feedback d-block">{{ $message }}</div>@enderror
</div>
<div class="row">
<div class="col-md-6 mb-5 form-control-validation">
<label class="form-label" for="nomor-dasar-hukum">Dasar hukum pendirian BUMDes (Peraturan Desa/SK) - Nomor</label>
<input type="text" class="form-control @error('nomor_dasar_hukum') is-invalid @enderror" id="nomor-dasar-hukum" name="nomor_dasar_hukum" value="{{ $fieldValue('nomor_dasar_hukum') }}" placeholder="Contoh: Perdes No. 02/2024">
@error('nomor_dasar_hukum')<div class="invalid-feedback">{{ $message }}</div>@enderror
</div>
<div class="col-md-3 mb-5 form-control-validation">
<label class="form-label" for="tahun-dasar-hukum">Tahun ditetapkan</label>
<input type="number" class="form-control @error('tahun_dasar_hukum') is-invalid @enderror" id="tahun-dasar-hukum" name="tahun_dasar_hukum" value="{{ $fieldValue('tahun_dasar_hukum') }}" placeholder="Contoh: 2024">
@error('tahun_dasar_hukum')<div class="invalid-feedback">{{ $message }}</div>@enderror
</div>
</div>
<div class="mb-5 form-control-validation">
<label class="form-label d-block fw-semibold">Bagaimana status keaktifan BUMDes?</label>
<div class="d-flex flex-column">
@foreach($statusKeaktifanOptions as $value => $label)
<div>
<div class="form-check">
<input class="form-check-input" type="radio" id="status-keaktifan-{{ $value }}" name="status_keaktifan" value="{{ $value }}" @checked($fieldValue('status_keaktifan') === $value)>
<label class="form-check-label" for="status-keaktifan-{{ $value }}">{{ $label }}</label>
</div>
</div>
@endforeach
</div>
@error('status_keaktifan')<div class="invalid-feedback d-block">{{ $message }}</div>@enderror
</div>
<div class="mb-5 form-control-validation" id="alasan-tidak-aktif-wrapper">
<label class="form-label" for="alasan-tidak-aktif">Jika tidak aktif/vakum, apa alasan utamanya?</label>
<textarea class="form-control @error('alasan_tidak_aktif') is-invalid @enderror" id="alasan-tidak-aktif" name="alasan_tidak_aktif" rows="2" placeholder="Tuliskan alasan utama BUMDes tidak aktif/vakum">{{ $fieldValue('alasan_tidak_aktif') }}</textarea>
@error('alasan_tidak_aktif')<div class="invalid-feedback">{{ $message }}</div>@enderror
</div>
<h5 class="pb-4 border-bottom mb-4 mt-4">Unit Usaha dan Dokumen Kelembagaan</h5>
<div class="mb-5 form-control-validation">
<label class="form-label d-block fw-semibold">Unit usaha yang dimiliki BUMDes saat ini (boleh pilih lebih dari satu)</label>
<div class="d-flex flex-column">
@foreach($unitUsahaOptions as $value => $label)
<div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="unit-usaha-{{ $value }}" name="unit_usaha[]" value="{{ $value }}" @checked(in_array($value, $arrayValue('unit_usaha'), true))>
<label class="form-check-label" for="unit-usaha-{{ $value }}">{{ $label }}</label>
</div>
</div>
@endforeach
</div>
@error('unit_usaha')<div class="invalid-feedback d-block">{{ $message }}</div>@enderror
</div>
<div class="row" id="unit-usaha-jasa-lainnya-wrapper">
<div class="col-md-6 mb-5 form-control-validation">
<label class="form-label" for="unit-usaha-jasa-lainnya">Jasa lainnya</label>
<input type="text" class="form-control @error('unit_usaha_jasa_lainnya') is-invalid @enderror" id="unit-usaha-jasa-lainnya" name="unit_usaha_jasa_lainnya" value="{{ $fieldValue('unit_usaha_jasa_lainnya') }}" placeholder="Sebutkan jasa lainnya">
@error('unit_usaha_jasa_lainnya')<div class="invalid-feedback">{{ $message }}</div>@enderror
</div>
</div>
<div class="mb-5 form-control-validation">
<label class="form-label d-block fw-semibold">Apakah ada unit usaha khusus yang menangani komoditas kopi/kakao/vanili/kemiri?</label>
<div class="d-flex flex-column">
@foreach($statusUnitKomoditasOptions as $value => $label)
<div>
<div class="form-check">
<input class="form-check-input" type="radio" id="status-unit-komoditas-{{ $value }}" name="status_unit_komoditas" value="{{ $value }}" @checked($fieldValue('status_unit_komoditas') === $value)>
<label class="form-check-label" for="status-unit-komoditas-{{ $value }}">{{ $label }}</label>
</div>
</div>
@endforeach
</div>
@error('status_unit_komoditas')<div class="invalid-feedback d-block">{{ $message }}</div>@enderror
</div>
<div class="row" id="nama-unit-komoditas-wrapper">
<div class="col-md-6 mb-5 form-control-validation">
<label class="form-label" for="nama-unit-komoditas">Nama unit usaha komoditas</label>
<input type="text" class="form-control @error('nama_unit_komoditas') is-invalid @enderror" id="nama-unit-komoditas" name="nama_unit_komoditas" value="{{ $fieldValue('nama_unit_komoditas') }}" placeholder="Contoh: Unit Pengolahan Kopi">
@error('nama_unit_komoditas')<div class="invalid-feedback">{{ $message }}</div>@enderror
</div>
</div>
<div class="mb-5 form-control-validation">
<label class="form-label d-block fw-semibold">Apakah ada dokumen kelembagaan (AD/ART)?</label>
<div class="d-flex flex-column">
@foreach($statusAdArtOptions as $value => $label)
<div>
<div class="form-check">
<input class="form-check-input" type="radio" id="status-ad-art-{{ $value }}" name="status_ad_art" value="{{ $value }}" @checked($fieldValue('status_ad_art') === $value)>
<label class="form-check-label" for="status-ad-art-{{ $value }}">{{ $label }}</label>
</div>
</div>
@endforeach
</div>
@error('status_ad_art')<div class="invalid-feedback d-block">{{ $message }}</div>@enderror
</div>
<div class="row" id="tahun-ad-art-wrapper">
<div class="col-md-3 mb-5 form-control-validation">
<label class="form-label" for="tahun-ad-art">Terakhir diperbarui tahun</label>
<input type="number" class="form-control @error('tahun_ad_art') is-invalid @enderror" id="tahun-ad-art" name="tahun_ad_art" value="{{ $fieldValue('tahun_ad_art') }}" placeholder="Contoh: 2024">
@error('tahun_ad_art')<div class="invalid-feedback">{{ $message }}</div>@enderror
</div>
</div>
<h5 class="pb-4 border-bottom mb-4 mt-4">Kepengurusan dan Staf</h5>
<label class="form-label d-block fw-semibold">Catat pengurus inti yang aktif menjabat saat ini</label>
<div class="table-responsive mb-5">
<table class="table table-bordered align-middle">
<thead>
<tr>
<th style="width: 30%;">Jabatan</th>
<th>Nama</th>
<th style="width: 22%;">Keaktifan</th>
</tr>
</thead>
<tbody>
@foreach($pengurusIntiRows as $key => $label)
@php
$savedRow = $rowValue('pengurus_inti', 'jabatan', $key);
$aktifValue = old("pengurus_inti.$loop->index.aktif", data_get($savedRow, 'aktif'));
@endphp
<tr>
<td>
<input type="hidden" name="pengurus_inti[{{ $loop->index }}][jabatan]" value="{{ $key }}">
{{ $label }}
</td>
<td>
<input type="text" class="form-control" name="pengurus_inti[{{ $loop->index }}][nama]" value="{{ old("pengurus_inti.$loop->index.nama", data_get($savedRow, 'nama')) }}">
</td>
<td>
<div class="d-flex flex-column">
<div class="form-check mb-0">
<input class="form-check-input" type="radio" id="pengurus-inti-{{ $key }}-aktif" name="pengurus_inti[{{ $loop->index }}][aktif]" value="1" @checked((string) $aktifValue === '1')>
<label class="form-check-label" for="pengurus-inti-{{ $key }}-aktif">Aktif</label>
</div>
<div class="form-check mb-0">
<input class="form-check-input" type="radio" id="pengurus-inti-{{ $key }}-tidak" name="pengurus_inti[{{ $loop->index }}][aktif]" value="0" @checked((string) $aktifValue === '0')>
<label class="form-check-label" for="pengurus-inti-{{ $key }}-tidak">Tidak</label>
</div>
</div>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
<div class="mb-5 form-control-validation">
<label class="form-label d-block fw-semibold">Apakah BUMDes memiliki staf/karyawan di luar pengurus inti?</label>
<div class="d-flex flex-column">
<div class="form-check">
<input class="form-check-input" type="radio" id="memiliki-staf-ya" name="memiliki_staf" value="1" @checked((string) $fieldValue('memiliki_staf') === '1')>
<label class="form-check-label" for="memiliki-staf-ya">Ya</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" id="memiliki-staf-tidak" name="memiliki_staf" value="0" @checked((string) $fieldValue('memiliki_staf') === '0')>
<label class="form-check-label" for="memiliki-staf-tidak">Tidak ada, dikelola langsung pengurus</label>
</div>
</div>
@error('memiliki_staf')<div class="invalid-feedback d-block">{{ $message }}</div>@enderror
</div>
<div class="row" id="staf-detail-wrapper">
<div class="col-md-3 mb-5 form-control-validation">
<label class="form-label" for="jumlah-staf">Jumlah staf</label>
<div class="input-group">
<input type="number" min="0" class="form-control @error('jumlah_staf') is-invalid @enderror" id="jumlah-staf" name="jumlah_staf" value="{{ $fieldValue('jumlah_staf') }}">
<span class="input-group-text">orang</span>
</div>
@error('jumlah_staf')<div class="invalid-feedback d-block">{{ $message }}</div>@enderror
</div>
<div class="col-md-9 mb-5 form-control-validation">
<label class="form-label" for="unit-usaha-staf">Jika ada, untuk unit usaha apa staf tersebut bekerja?</label>
<input type="text" class="form-control @error('unit_usaha_staf') is-invalid @enderror" id="unit-usaha-staf" name="unit_usaha_staf" value="{{ $fieldValue('unit_usaha_staf') }}" placeholder="Contoh: Unit perdagangan/toko desa">
@error('unit_usaha_staf')<div class="invalid-feedback">{{ $message }}</div>@enderror
</div>
</div>
<h5 class="pb-4 border-bottom mb-4 mt-4">Modal dan Aset</h5>
<div class="row">
<div class="col-md-4 mb-5 form-control-validation">
<label class="form-label" for="modal-awal">Besaran modal awal BUMDes (jika diketahui)</label>
<div class="input-group">
<span class="input-group-text">Rp</span>
<input type="text" inputmode="numeric" class="form-control rupiah-input @error('modal_awal') is-invalid @enderror" id="modal-awal" name="modal_awal" value="{{ $rupiahValue($fieldValue('modal_awal')) }}">
</div>
@error('modal_awal')<div class="invalid-feedback d-block">{{ $message }}</div>@enderror
</div>
<div class="col-md-3 mb-5 form-control-validation">
<label class="form-label" for="tahun-modal-awal">Tahun diterima</label>
<input type="number" class="form-control @error('tahun_modal_awal') is-invalid @enderror" id="tahun-modal-awal" name="tahun_modal_awal" value="{{ $fieldValue('tahun_modal_awal') }}" placeholder="Contoh: 2024">
@error('tahun_modal_awal')<div class="invalid-feedback">{{ $message }}</div>@enderror
</div>
</div>
<div class="mb-5 form-control-validation">
<label class="form-label d-block fw-semibold">Apakah BUMDes pernah menerima penambahan modal sejak pertama kali berdiri?</label>
<div class="d-flex flex-column">
<div class="form-check">
<input class="form-check-input" type="radio" id="penambahan-modal-ya" name="pernah_penambahan_modal" value="1" @checked((string) $fieldValue('pernah_penambahan_modal') === '1')>
<label class="form-check-label" for="penambahan-modal-ya">Ya, pernah</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" id="penambahan-modal-tidak" name="pernah_penambahan_modal" value="0" @checked((string) $fieldValue('pernah_penambahan_modal') === '0')>
<label class="form-check-label" for="penambahan-modal-tidak">Tidak pernah menerima penambahan modal</label>
</div>
</div>
@error('pernah_penambahan_modal')<div class="invalid-feedback d-block">{{ $message }}</div>@enderror
</div>
<div id="penambahan-modal-wrapper">
<label class="form-label d-block fw-semibold">Jika BUMDes pernah menerima penambahan modal, isi rincian penambahan modal 3 tahun terakhir</label>
<div class="table-responsive mb-5">
<table class="table table-bordered align-middle">
<thead>
<tr>
<th>Tahun</th>
<th>Jumlah (Rp)</th>
</tr>
</thead>
<tbody>
@for($i = 0; $i < 3; $i++)
@php $savedRow = data_get($fieldValue('penambahan_modal', []), $i, []); @endphp
<tr>
<td>
<input type="number" class="form-control" name="penambahan_modal[{{ $i }}][tahun]" value="{{ old("penambahan_modal.$i.tahun", data_get($savedRow, 'tahun')) }}" placeholder="Contoh: 2024">
</td>
<td>
<div class="input-group">
<span class="input-group-text">Rp</span>
<input type="text" inputmode="numeric" class="form-control rupiah-input" name="penambahan_modal[{{ $i }}][jumlah]" value="{{ $rupiahValue(old("penambahan_modal.$i.jumlah", data_get($savedRow, 'jumlah'))) }}">
</div>
</td>
</tr>
@endfor
</tbody>
</table>
</div>
</div>
<label class="form-label d-block fw-semibold">Aset produktif yang dimiliki BUMDes (boleh pilih lebih dari satu, lewati jika tidak ada)</label>
<div class="table-responsive mb-5">
<table class="table table-bordered align-middle">
<thead>
<tr>
<th style="width: 34%;">Aset Produktif</th>
<th>Jumlah</th>
<th>Kondisi</th>
</tr>
</thead>
<tbody>
@foreach($asetProduktifRows as $key => $label)
@php
$savedRow = $rowValue('aset_produktif', 'aset', $key);
$dipilih = old("aset_produktif.$loop->index.dipilih", data_get($savedRow, 'dipilih'));
@endphp
<tr>
<td>
<input type="hidden" name="aset_produktif[{{ $loop->index }}][aset]" value="{{ $key }}">
<div class="form-check">
<input class="form-check-input aset-produktif-check" type="checkbox" id="aset-produktif-{{ $key }}" name="aset_produktif[{{ $loop->index }}][dipilih]" value="1" data-target="{{ $key }}" @checked((string) $dipilih === '1')>
<label class="form-check-label" for="aset-produktif-{{ $key }}">{{ $label }}</label>
</div>
@if($key === 'lainnya')
<input type="text" class="form-control mt-2" id="aset-produktif-lainnya" name="aset_produktif_lainnya" value="{{ $fieldValue('aset_produktif_lainnya') }}" placeholder="Sebutkan aset lainnya">
@endif
</td>
<td>
<input type="text" class="form-control" name="aset_produktif[{{ $loop->index }}][jumlah]" value="{{ old("aset_produktif.$loop->index.jumlah", data_get($savedRow, 'jumlah')) }}">
</td>
<td>
<input type="text" class="form-control" name="aset_produktif[{{ $loop->index }}][kondisi]" value="{{ old("aset_produktif.$loop->index.kondisi", data_get($savedRow, 'kondisi')) }}" placeholder="Contoh: Baik, rusak ringan">
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
<div class="mb-5 form-control-validation">
<label class="form-label d-block fw-semibold">Status kepemilikan aset (lewati jika tidak ada)</label>
<div class="d-flex flex-column">
@foreach($statusKepemilikanAsetOptions as $value => $label)
<div>
<div class="form-check">
<input class="form-check-input" type="radio" id="status-kepemilikan-aset-{{ $value }}" name="status_kepemilikan_aset" value="{{ $value }}" @checked($fieldValue('status_kepemilikan_aset') === $value)>
<label class="form-check-label" for="status-kepemilikan-aset-{{ $value }}">{{ $label }}</label>
</div>
</div>
@endforeach
</div>
@error('status_kepemilikan_aset')<div class="invalid-feedback d-block">{{ $message }}</div>@enderror
</div>
<label class="form-label d-block fw-semibold">Ketersediaan infrastruktur pascapanen untuk komoditas kopi/kakao/vanili/kemiri</label>
<div class="table-responsive mb-5">
<table class="table table-bordered align-middle">
<thead>
<tr>
<th style="width: 34%;">Infrastruktur Pascapanen</th>
<th>Jumlah</th>
<th style="width: 34%;">Kondisi</th>
</tr>
</thead>
<tbody>
@foreach($infrastrukturPascapanenRows as $key => $label)
@php
$savedRow = $rowValue('infrastruktur_pascapanen', 'infrastruktur', $key);
$dipilih = old("infrastruktur_pascapanen.$loop->index.dipilih", data_get($savedRow, 'dipilih'));
$kondisiValue = old("infrastruktur_pascapanen.$loop->index.kondisi", data_get($savedRow, 'kondisi'));
@endphp
<tr>
<td>
<input type="hidden" name="infrastruktur_pascapanen[{{ $loop->index }}][infrastruktur]" value="{{ $key }}">
<div class="form-check">
<input class="form-check-input infrastruktur-check" type="checkbox" id="infrastruktur-{{ $key }}" name="infrastruktur_pascapanen[{{ $loop->index }}][dipilih]" value="1" data-target="{{ $key }}" @checked((string) $dipilih === '1')>
<label class="form-check-label" for="infrastruktur-{{ $key }}">{{ $label }}</label>
</div>
@if($key === 'lainnya')
<input type="text" class="form-control mt-2" id="infrastruktur-pascapanen-lainnya" name="infrastruktur_pascapanen_lainnya" value="{{ $fieldValue('infrastruktur_pascapanen_lainnya') }}" placeholder="Sebutkan infrastruktur lainnya">
@endif
</td>
<td>
<input type="text" class="form-control" name="infrastruktur_pascapanen[{{ $loop->index }}][jumlah]" value="{{ old("infrastruktur_pascapanen.$loop->index.jumlah", data_get($savedRow, 'jumlah')) }}">
</td>
<td>
<div class="d-flex flex-column">
@foreach($kondisiInfrastrukturOptions as $kondisi => $kondisiLabel)
<div class="form-check mb-0">
<input class="form-check-input" type="radio" id="infrastruktur-{{ $key }}-{{ $kondisi }}" name="infrastruktur_pascapanen[{{ $loop->parent->index }}][kondisi]" value="{{ $kondisi }}" @checked($kondisiValue === $kondisi)>
<label class="form-check-label" for="infrastruktur-{{ $key }}-{{ $kondisi }}">{{ $kondisiLabel }}</label>
</div>
@endforeach
</div>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
<div class="mt-4 text-end">
<a href="{{ route('bumdes.index') }}" class="btn btn-secondary me-2 wave-effect wave-light">Kembali</a>
<button type="submit" class="btn btn-success data-submit wave-effect wave-light">
<i class="ti tabler-device-floppy me-2"></i>Simpan Kelembagaan dan Aktor
</button>
</div>
</div>
</div>
</form>
</div>
@endsection
@push('scripts')
<script>
'use strict';
$(document).ready(function () {
function onlyDigits(value) {
return String(value || '').replace(/\D/g, '');
}
function formatRupiah(value) {
return onlyDigits(value).replace(/\B(?=(\d{3})+(?!\d))/g, '.');
}
function toggleWrapper($wrapper, enabled) {
$wrapper.toggleClass('d-none', !enabled);
setFieldsEnabled($wrapper, enabled);
}
function setFieldsEnabled($wrapper, enabled) {
$wrapper.find('input, textarea, select').prop('disabled', !enabled);
if (!enabled) {
$wrapper.find('input:not([type="radio"]):not([type="checkbox"]), textarea').val('');
$wrapper.find('input[type="radio"], input[type="checkbox"]').prop('checked', false);
}
}
function syncStatusKeaktifan() {
setFieldsEnabled($('#alasan-tidak-aktif-wrapper'), $('input[name="status_keaktifan"]:checked').val() === 'tidak_aktif_vakum');
}
function syncUnitUsahaLainnya() {
toggleWrapper($('#unit-usaha-jasa-lainnya-wrapper'), $('input[name="unit_usaha[]"][value="jasa_lainnya"]').is(':checked'));
}
function syncUnitKomoditas() {
toggleWrapper($('#nama-unit-komoditas-wrapper'), $('input[name="status_unit_komoditas"]:checked').val() === 'ya');
}
function syncAdArt() {
toggleWrapper($('#tahun-ad-art-wrapper'), $('input[name="status_ad_art"]:checked').val() === 'ada_lengkap');
}
function syncStaf() {
setFieldsEnabled($('#staf-detail-wrapper'), $('input[name="memiliki_staf"]:checked').val() === '1');
}
function syncPenambahanModal() {
setFieldsEnabled($('#penambahan-modal-wrapper'), $('input[name="pernah_penambahan_modal"]:checked').val() === '1');
}
function syncAsetLainnya() {
$('#aset-produktif-lainnya').prop('disabled', !$('input.aset-produktif-check[data-target="lainnya"]').is(':checked'));
if ($('#aset-produktif-lainnya').prop('disabled')) {
$('#aset-produktif-lainnya').val('');
}
}
function syncInfrastrukturLainnya() {
$('#infrastruktur-pascapanen-lainnya').prop('disabled', !$('input.infrastruktur-check[data-target="lainnya"]').is(':checked'));
if ($('#infrastruktur-pascapanen-lainnya').prop('disabled')) {
$('#infrastruktur-pascapanen-lainnya').val('');
}
}
$(document).on('input', '.rupiah-input', function () {
const cursorAtEnd = this.selectionStart === this.value.length;
this.value = formatRupiah(this.value);
if (cursorAtEnd) {
this.setSelectionRange(this.value.length, this.value.length);
}
});
$('.rupiah-input').each(function () {
this.value = formatRupiah(this.value);
});
$('input[name="status_keaktifan"]').on('change', syncStatusKeaktifan);
$('input[name="unit_usaha[]"]').on('change', syncUnitUsahaLainnya);
$('input[name="status_unit_komoditas"]').on('change', syncUnitKomoditas);
$('input[name="status_ad_art"]').on('change', syncAdArt);
$('input[name="memiliki_staf"]').on('change', syncStaf);
$('input[name="pernah_penambahan_modal"]').on('change', syncPenambahanModal);
$('.aset-produktif-check').on('change', syncAsetLainnya);
$('.infrastruktur-check').on('change', syncInfrastrukturLainnya);
$('#kelembagaanAktorForm').on('submit', function () {
$(this).find('.rupiah-input').each(function () {
this.value = onlyDigits(this.value);
});
});
syncStatusKeaktifan();
syncUnitUsahaLainnya();
syncUnitKomoditas();
syncAdArt();
syncStaf();
syncPenambahanModal();
syncAsetLainnya();
syncInfrastrukturLainnya();
});
</script>
@endpush
@@ -164,7 +164,7 @@
if (provinsiId) {
$.ajax({
url: '{{ route("kabupatenkota.byprovinsi", ":id") }}'.replace(':id', provinsiId),
url: '{{ route("kabupatenkota.byprovinsi", ":id") }}'.replace(':id', provinsiId) + '?all_wilayah=1',
method: 'GET',
success: function(data) {
$(`${prefix}-kabupaten`).empty().append('<option value="">Pilih Kabupaten/Kota</option>');
@@ -184,7 +184,7 @@
if (kabupatenId) {
$.ajax({
url: '{{ route("kecamatan.bykabupatenkota", ":id") }}'.replace(':id', kabupatenId),
url: '{{ route("kecamatan.bykabupatenkota", ":id") }}'.replace(':id', kabupatenId) + '?all_wilayah=1',
method: 'GET',
success: function(data) {
$(`${prefix}-kecamatan`).empty().append('<option value="">Pilih Kecamatan</option>');
@@ -203,7 +203,7 @@
if (kecamatanId) {
$.ajax({
url: '{{ route("desakelurahan.bykecamatan", ":id") }}'.replace(':id', kecamatanId),
url: '{{ route("desakelurahan.bykecamatan", ":id") }}'.replace(':id', kecamatanId) + '?all_wilayah=1',
method: 'GET',
success: function(data) {
$(`${prefix}-desa`).empty().append('<option value="">Pilih Desa/Kelurahan</option>');
+12 -8
View File
@@ -238,6 +238,10 @@
@push('scripts')
<script>
document.addEventListener('DOMContentLoaded', function() {
const pastelChart = config.chartColors || {};
const pastelSemantic = pastelChart.semantic || {};
const pastelCategory = pastelChart.categories || {};
const gridColor = pastelChart.grid || '#E8EDF5';
// 1. Inisialisasi Icons
if(typeof lucide !== 'undefined') {
@@ -255,7 +259,7 @@
{
label: 'Desa Mengadopsi (Realisasi)',
data: [0, 3, 8, 15], // Kumulatif: 3 Desa Inti + 12 Baru
backgroundColor: '#8b5cf6', // Purple-500
backgroundColor: pastelCategory.policyPurple || '#9D8EF0',
borderRadius: 4,
order: 2
},
@@ -263,12 +267,12 @@
label: 'Target Perluasan ToC',
type: 'line',
data: [3, 5, 10, 15],
borderColor: '#d946ef', // Fuchsia-500
borderColor: pastelCategory.policyPink || '#F47C86',
backgroundColor: 'transparent',
borderWidth: 3,
borderDash: [5, 5],
tension: 0.3,
pointBackgroundColor: '#d946ef',
pointBackgroundColor: pastelCategory.policyPink || '#F47C86',
order: 1
}
]
@@ -280,7 +284,7 @@
scales: {
y: {
beginAtZero: true,
grid: { color: '#f3f4f6' },
grid: { color: gridColor },
ticks: { stepSize: 5 }
},
x: { grid: { display: false } }
@@ -309,10 +313,10 @@
datasets: [{
data: [4, 2, 1, 1],
backgroundColor: [
'#f59e0b', // Amber-500
'#10b981', // Emerald-500
'#3b82f6', // Blue-500
'#8b5cf6' // Purple-500
pastelSemantic.warning || '#FFB46F',
pastelSemantic.success || '#6FD68F',
pastelSemantic.info || '#5FC7D6',
pastelCategory.policyPurple || '#9D8EF0'
],
borderWidth: 2,
borderColor: '#ffffff',
+27 -27
View File
@@ -7,9 +7,9 @@
transition: transform .2s ease;
}
.tk-stat:hover { transform: translateX(4px); }
.tk-stat.green { border-left-color: #28a745; }
.tk-stat.blue { border-left-color: #007bff; }
.tk-stat.amber { border-left-color: #fd7e14; }
.tk-stat.green { border-left-color: #6FD68F; }
.tk-stat.blue { border-left-color: #5FC7D6; }
.tk-stat.amber { border-left-color: #FFB46F; }
.tk-stat-lbl {
font-size: 10px; text-transform: uppercase;
@@ -88,38 +88,38 @@
'new_month' => 12,
'enumerators' => [
['name'=>'Enumerator Ngada 1','done'=>72,'target'=>80,'color'=>'#28a745','bg'=>'rgba(40,167,69,.15)','tx'=>'#28a745'],
['name'=>'Enumerator Ngada 2','done'=>58,'target'=>80,'color'=>'#007bff','bg'=>'rgba(0,123,255,.15)','tx'=>'#007bff'],
['name'=>'Enumerator Ngada 3','done'=>48,'target'=>80,'color'=>'#fd7e14','bg'=>'rgba(253,126,20,.15)','tx'=>'#fd7e14'],
['name'=>'Enumerator Ngada 4','done'=>36,'target'=>80,'color'=>'#dc3545','bg'=>'rgba(220,53,69,.15)','tx'=>'#dc3545'],
['name'=>'Enumerator Ngada 1','done'=>72,'target'=>80,'color'=>'#6FD68F','bg'=>'rgba(111,214,143,.24)','tx'=>'#6FD68F'],
['name'=>'Enumerator Ngada 2','done'=>58,'target'=>80,'color'=>'#5FC7D6','bg'=>'rgba(95,199,214,.24)','tx'=>'#5FC7D6'],
['name'=>'Enumerator Ngada 3','done'=>48,'target'=>80,'color'=>'#FFB46F','bg'=>'rgba(255,180,111,.26)','tx'=>'#FFB46F'],
['name'=>'Enumerator Ngada 4','done'=>36,'target'=>80,'color'=>'#F47C86','bg'=>'rgba(244,124,134,.26)','tx'=>'#F47C86'],
],
'recent' => [
['code'=>'TKD-2026-0178','name'=>'Markus Bela', 'init'=>'MB','desa'=>'Wangka', 'date'=>'24 Jun 2026','pct'=>100,'status'=>'completed', 'bg'=>'rgba(40,167,69,.15)','tx'=>'#28a745'],
['code'=>'TKD-2026-0177','name'=>'Theresia Nuka','init'=>'TN','desa'=>'Langa', 'date'=>'23 Jun 2026','pct'=>67, 'status'=>'in_progress','bg'=>'rgba(0,123,255,.15)','tx'=>'#007bff'],
['code'=>'TKD-2026-0176','name'=>'Yosef Meo', 'init'=>'YM','desa'=>'Bobo', 'date'=>'22 Jun 2026','pct'=>100,'status'=>'completed', 'bg'=>'rgba(40,167,69,.15)','tx'=>'#28a745'],
['code'=>'TKD-2026-0175','name'=>'Maria Seda', 'init'=>'MS','desa'=>'Tana Wolo','date'=>'21 Jun 2026','pct'=>22, 'status'=>'draft', 'bg'=>'rgba(253,126,20,.15)','tx'=>'#fd7e14'],
['code'=>'TKD-2026-0174','name'=>'Rafael Bolo', 'init'=>'RB','desa'=>'Wangka', 'date'=>'20 Jun 2026','pct'=>11, 'status'=>'draft', 'bg'=>'rgba(220,53,69,.15)','tx'=>'#dc3545'],
['code'=>'TKD-2026-0178','name'=>'Markus Bela', 'init'=>'MB','desa'=>'Wangka', 'date'=>'24 Jun 2026','pct'=>100,'status'=>'completed', 'bg'=>'rgba(111,214,143,.24)','tx'=>'#6FD68F'],
['code'=>'TKD-2026-0177','name'=>'Theresia Nuka','init'=>'TN','desa'=>'Langa', 'date'=>'23 Jun 2026','pct'=>67, 'status'=>'in_progress','bg'=>'rgba(95,199,214,.24)','tx'=>'#5FC7D6'],
['code'=>'TKD-2026-0176','name'=>'Yosef Meo', 'init'=>'YM','desa'=>'Bobo', 'date'=>'22 Jun 2026','pct'=>100,'status'=>'completed', 'bg'=>'rgba(111,214,143,.24)','tx'=>'#6FD68F'],
['code'=>'TKD-2026-0175','name'=>'Maria Seda', 'init'=>'MS','desa'=>'Tana Wolo','date'=>'21 Jun 2026','pct'=>22, 'status'=>'draft', 'bg'=>'rgba(255,180,111,.26)','tx'=>'#FFB46F'],
['code'=>'TKD-2026-0174','name'=>'Rafael Bolo', 'init'=>'RB','desa'=>'Wangka', 'date'=>'20 Jun 2026','pct'=>11, 'status'=>'draft', 'bg'=>'rgba(244,124,134,.26)','tx'=>'#F47C86'],
],
'komoditas' => [
['label'=>'Kopi', 'total'=>148,'pct'=>60,'color'=>'#28a745','arc'=>174],
['label'=>'Kakao', 'total'=>82, 'pct'=>33,'color'=>'#007bff','arc'=>96],
['label'=>'Vanili','total'=>33, 'pct'=>13,'color'=>'#fd7e14','arc'=>38],
['label'=>'Kemiri','total'=>22, 'pct'=>9, 'color'=>'#6f42c1','arc'=>26],
['label'=>'Kopi', 'total'=>148,'pct'=>60,'color'=>'#6FD68F','arc'=>174],
['label'=>'Kakao', 'total'=>82, 'pct'=>33,'color'=>'#9D8EF0','arc'=>96],
['label'=>'Vanili','total'=>33, 'pct'=>13,'color'=>'#FFB46F','arc'=>38],
['label'=>'Kemiri','total'=>22, 'pct'=>9, 'color'=>'#9EA6B0','arc'=>26],
],
'kecamatan' => [
['name'=>'Bajawa','total'=>89,'pct'=>78,'color'=>'#28a745'],
['name'=>'Soa', 'total'=>68,'pct'=>59,'color'=>'#007bff'],
['name'=>'Golewa','total'=>57,'pct'=>50,'color'=>'#fd7e14'],
['name'=>'Bajawa','total'=>89,'pct'=>78,'color'=>'#6FD68F'],
['name'=>'Soa', 'total'=>68,'pct'=>59,'color'=>'#5FC7D6'],
['name'=>'Golewa','total'=>57,'pct'=>50,'color'=>'#FFB46F'],
],
'pending' => [
['name'=>'Theresia Nuka','section'=>'D4-K3','pct'=>67,'bg'=>'rgba(0,123,255,.15)','tx'=>'#007bff'],
['name'=>'Maria Seda', 'section'=>'D2-K2','pct'=>22,'bg'=>'rgba(253,126,20,.15)','tx'=>'#fd7e14'],
['name'=>'Rafael Bolo', 'section'=>'D2-K1','pct'=>11,'bg'=>'rgba(220,53,69,.15)', 'tx'=>'#dc3545'],
['name'=>'Antonius Wea', 'section'=>'D2-K1','pct'=>0, 'bg'=>'rgba(220,53,69,.15)', 'tx'=>'#dc3545'],
['name'=>'Theresia Nuka','section'=>'D4-K3','pct'=>67,'bg'=>'rgba(95,199,214,.24)','tx'=>'#5FC7D6'],
['name'=>'Maria Seda', 'section'=>'D2-K2','pct'=>22,'bg'=>'rgba(255,180,111,.26)','tx'=>'#FFB46F'],
['name'=>'Rafael Bolo', 'section'=>'D2-K1','pct'=>11,'bg'=>'rgba(244,124,134,.26)', 'tx'=>'#F47C86'],
['name'=>'Antonius Wea', 'section'=>'D2-K1','pct'=>0, 'bg'=>'rgba(244,124,134,.26)', 'tx'=>'#F47C86'],
],
'pending_more' => 32,
];
@@ -258,10 +258,10 @@
@foreach($d['recent'] as $s)
@php
$bc = match(true) {
$s['pct'] === 100 => '#28a745',
$s['pct'] >= 50 => '#007bff',
$s['pct'] >= 20 => '#fd7e14',
default => '#dc3545',
$s['pct'] === 100 => '#6FD68F',
$s['pct'] >= 50 => '#5FC7D6',
$s['pct'] >= 20 => '#FFB46F',
default => '#F47C86',
};
@endphp
<tr>
File diff suppressed because it is too large Load Diff
+11 -7
View File
@@ -208,6 +208,10 @@
@push('scripts')
<script>
document.addEventListener('DOMContentLoaded', function() {
const pastelChart = config.chartColors || {};
const pastelSemantic = pastelChart.semantic || {};
const pastelCategory = pastelChart.categories || {};
const gridColor = pastelChart.grid || '#E8EDF5';
// 1. Inisialisasi Icons
if(typeof lucide !== 'undefined') {
@@ -225,7 +229,7 @@
{
label: 'Mentah/Gelondong (Rp)',
data: [35000, 200000, 25000, 15000],
backgroundColor: '#9ca3af', // Gray
backgroundColor: pastelSemantic.gray || '#9EA6B0',
borderRadius: { topLeft: 4, topRight: 4, bottomLeft: 0, bottomRight: 0 },
barPercentage: 0.6,
categoryPercentage: 0.8
@@ -233,7 +237,7 @@
{
label: 'Produk Olahan (Rp)',
data: [120000, 1500000, 80000, 45000],
backgroundColor: '#d97706', // Amber-600
backgroundColor: pastelSemantic.warning || '#FFB46F',
borderRadius: { topLeft: 4, topRight: 4, bottomLeft: 0, bottomRight: 0 },
barPercentage: 0.6,
categoryPercentage: 0.8
@@ -246,7 +250,7 @@
scales: {
y: {
beginAtZero: true,
grid: { color: '#f3f4f6' },
grid: { color: gridColor },
ticks: {
callback: function(value) {
if (value >= 1000000) return 'Rp ' + (value/1000000) + ' Jt';
@@ -282,8 +286,8 @@
{
label: 'Kopi Bubuk/Roasted',
data: [12, 15, 18, 25, 30, 45],
borderColor: '#6F4E37',
backgroundColor: 'rgba(111, 78, 55, 0.1)',
borderColor: pastelCategory.coffee || '#6FD68F',
backgroundColor: 'rgba(104, 193, 124, 0.16)',
fill: true,
tension: 0.4,
borderWidth: 2
@@ -291,7 +295,7 @@
{
label: 'Kakao Fermentasi',
data: [5, 8, 12, 15, 22, 28],
borderColor: '#D2691E',
borderColor: pastelCategory.cacao || '#9D8EF0',
backgroundColor: 'transparent',
tension: 0.4,
borderWidth: 2
@@ -305,7 +309,7 @@
scales: {
y: {
title: { display: true, text: 'Volume (Ton)' },
grid: { color: '#f3f4f6' }
grid: { color: gridColor }
},
x: { grid: { display: false } }
},
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -212,6 +212,9 @@
@push('scripts')
<script>
document.addEventListener('DOMContentLoaded', function() {
const pastelChart = config.chartColors || {};
const pastelCategory = pastelChart.categories || {};
const gridColor = pastelChart.grid || '#E8EDF5';
// 1. Inisialisasi Icons
if(typeof lucide !== 'undefined') {
@@ -230,8 +233,8 @@
label: 'Total Produksi Lahan (Ton)',
type: 'line',
data: [100, 110, 125, 145],
borderColor: '#10b981', // Emerald-500
backgroundColor: '#10b981',
borderColor: pastelCategory.marketGreen || '#6FD68F',
backgroundColor: pastelCategory.marketGreen || '#6FD68F',
borderWidth: 3,
tension: 0.3,
fill: false
@@ -239,14 +242,14 @@
{
label: 'Tengkulak / Pasar Bebas',
data: [80, 65, 40, 25],
backgroundColor: '#ef4444', // Red-500
backgroundColor: pastelCategory.marketRed || '#F47C86',
stack: 'Stack 0',
borderRadius: { topLeft: 0, topRight: 0, bottomLeft: 4, bottomRight: 4 }
},
{
label: 'Kelembagaan (Koperasi/BUMDes)',
data: [20, 45, 85, 120],
backgroundColor: '#3b82f6', // Blue-500
backgroundColor: pastelCategory.marketBlue || '#5FC7D6',
stack: 'Stack 0',
borderRadius: { topLeft: 4, topRight: 4, bottomLeft: 0, bottomRight: 0 }
}
@@ -260,7 +263,7 @@
y: {
stacked: true,
beginAtZero: true,
grid: { color: '#f3f4f6' }
grid: { color: gridColor }
},
x: {
stacked: true,
@@ -292,20 +295,20 @@
{
label: 'Kopi & Kakao',
data: [150, 280, 320, 500, 650, 850],
borderColor: '#6F4E37',
borderColor: pastelCategory.coffee || '#6FD68F',
backgroundColor: 'transparent',
tension: 0.4,
borderWidth: 2,
pointBackgroundColor: '#6F4E37'
pointBackgroundColor: pastelCategory.coffee || '#6FD68F'
},
{
label: 'Vanili & Kemiri',
data: [50, 120, 210, 350, 420, 600],
borderColor: '#eab308',
borderColor: pastelCategory.vanilla || '#FFB46F',
backgroundColor: 'transparent',
tension: 0.4,
borderWidth: 2,
pointBackgroundColor: '#eab308'
pointBackgroundColor: pastelCategory.vanilla || '#FFB46F'
}
]
},
@@ -316,7 +319,7 @@
scales: {
y: {
title: { display: false },
grid: { color: '#f3f4f6' },
grid: { color: gridColor },
ticks: {
callback: function(value) {
return 'Rp ' + value + ' Jt';
+965
View File
@@ -0,0 +1,965 @@
@extends('layout.main')
@push('style')
<style>
.pasar-filter-card .filter-item {
min-width: 190px;
}
.pasar-kpi-value {
color: var(--bs-heading-color);
font-size: 1.6rem;
font-weight: 600;
line-height: 1.2;
overflow-wrap: anywhere;
}
.pasar-kpi-label {
color: var(--bs-heading-color);
font-size: 0.9375rem;
font-weight: 400;
}
.pasar-kpi-desc {
font-size: 0.8125rem;
line-height: 1.4;
}
.chart-group-header-toggle {
cursor: pointer;
background-color: rgba(var(--bs-primary-rgb, 115, 103, 240), 0.08);
border-inline-start: 4px solid var(--bs-primary, #9D8EF0);
box-shadow: none;
transition: background-color 0.2s ease;
}
.chart-group-header-toggle:hover {
background-color: rgba(var(--bs-primary-rgb, 115, 103, 240), 0.13);
}
.chart-group-header-toggle .card-body {
padding: 12px 16px;
}
.chart-group-header-toggle h5 {
font-size: 1rem;
font-weight: 600;
margin-bottom: 2px;
}
.chart-group-header-toggle:focus-visible {
outline: 2px solid var(--bs-primary);
outline-offset: 3px;
}
.chart-group-icon-toggle {
width: 34px;
height: 34px;
flex-shrink: 0;
}
.chart-group-icon-toggle .icon-base {
font-size: 24px;
width: 24px;
height: 24px;
}
.chart-group-panel.show,
.chart-group-panel.collapsing {
margin-bottom: 1.5rem;
}
.pasar-chart-card .card-title h5 {
color: var(--bs-heading-color);
font-size: 1.125rem;
font-weight: 500;
line-height: 1.35;
letter-spacing: 0;
}
.pasar-chart-card .card-title small,
.chart-group-header-toggle p {
font-size: 0.8125rem;
line-height: 1.4;
}
.pasar-chart {
min-height: 330px;
}
.pasar-chart-empty {
min-height: 330px;
display: flex;
align-items: center;
justify-content: center;
color: var(--bs-secondary-color);
font-size: 0.8125rem;
text-align: center;
}
.pasar-chart-card .apexcharts-tooltip {
background: transparent !important;
border: none !important;
box-shadow: none !important;
overflow: visible !important;
white-space: normal;
max-width: 260px;
}
.pasar-chart-tooltip {
border-radius: 5px;
box-shadow: 0 4px 12px rgba(0, 0, 0, .15);
padding: 8px 12px;
font-size: 13px;
font-weight: 500;
line-height: 1.25;
}
.pasar-trend-tooltip-row {
display: flex;
justify-content: space-between;
gap: 12px;
margin-top: 6px;
}
@media (max-width: 575.98px) {
.pasar-filter-card .filter-item {
width: 100%;
}
}
.market-info-button {
display: inline-flex;
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
flex: 0 0 20px;
padding: 0;
border: 1px solid var(--bs-secondary-color);
border-radius: 50%;
background: transparent;
color: var(--bs-secondary-color);
font-size: 12px;
font-weight: 600;
cursor: pointer;
}
.market-info-button:hover, .market-info-button:focus-visible {
color: var(--bs-primary);
border-color: var(--bs-primary);
outline: 2px solid rgba(var(--bs-primary-rgb), .2);
outline-offset: 2px;
}
.international-provider-logo {
width: 48px;
height: 48px;
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 12px;
background: #fff;
border: 1px solid rgba(0, 0, 0, 0.08);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.04);
}
.international-provider-logo[hidden] { display: none; }
.international-provider-logo img { object-fit: contain; }
.international-market-track {
display: flex;
gap: 1.25rem;
overflow-x: auto;
scroll-snap-type: none;
scrollbar-width: none;
padding: 2px 2px 16px;
}
.international-market-track::-webkit-scrollbar { display: none; }
.international-market-card {
flex: 0 0 calc((100% - 2.5rem) / 3);
min-width: 0;
scroll-snap-align: start;
}
@media (max-width: 991.98px) {
.international-market-card { flex-basis: calc((100% - 1.25rem) / 2); }
}
@media (max-width: 575.98px) {
.international-market-card { flex-basis: 100%; }
}
</style>
@endpush
@section('content')
<div class="container-xxl flex-grow-1 container-p-y">
<div class="row g-4 mb-4">
<form id="form-filter" class="col-12" method="GET" action="{{ route('dashboard.pasar') }}">
<div class="card card-border-shadow-primary pasar-filter-card">
<div class="card-body py-3">
<div class="d-flex align-items-center gap-2 flex-wrap">
<div class="d-flex align-items-center me-2">
<i class="icon-base ti tabler-map-2 me-2"></i>
<span class="fw-medium">Wilayah</span>
</div>
<div class="filter-item">
<select id="filter-provinsi" class="form-select form-select-sm" name="provinsi_id" data-default="{{ $provinsi_id }}">
<option value="">Semua Provinsi</option>
@foreach($provinsi as $p)
<option value="{{ $p->provinsi_id }}" {{ old('provinsi_id', $provinsi_id) == $p->provinsi_id ? 'selected' : '' }}>
{{ $p->provinsi }}
</option>
@endforeach
</select>
</div>
<div class="filter-item">
<select id="filter-kabkota" class="form-select form-select-sm" name="kabupatenKota_id" data-default="{{ $kabupatenKota_id }}" disabled>
<option value="">Semua Kabupaten/Kota</option>
</select>
</div>
<div class="filter-item">
<select id="filter-kecamatan" class="form-select form-select-sm" name="kecamatan_id" data-default="{{ $kecamatan_id }}" disabled>
<option value="">Semua Kecamatan</option>
</select>
</div>
<div class="filter-item">
<select id="filter-desa" class="form-select form-select-sm" name="desaKelurahan_id" data-default="{{ $desaKelurahan_id }}" disabled>
<option value="">Semua Desa/Kelurahan</option>
</select>
</div>
</div>
<hr class="my-3">
<div class="d-flex align-items-center gap-2 flex-wrap">
<div class="d-flex align-items-center me-2">
<i class="icon-base ti tabler-adjustments-horizontal me-2"></i>
<span class="fw-medium">Data</span>
</div>
<div class="filter-item">
<select id="filter-komoditas" class="form-select form-select-sm" name="komoditas_id">
<option value="">Semua Komoditas</option>
@foreach ($komoditas as $item)
<option value="{{ $item->id }}" {{ old('komoditas_id', $komoditas_id) == $item->id ? 'selected' : '' }}>
{{ $item->nama }}
</option>
@endforeach
</select>
</div>
<div class="filter-item">
<select id="filter-segmen" class="form-select form-select-sm" name="segmen">
<option value="">Semua Segmen</option>
@foreach ($segments as $segment)
<option value="{{ $segment['name'] }}" {{ old('segmen', $segmen) == $segment['name'] ? 'selected' : '' }}>
{{ $segment['name'] }}
</option>
@endforeach
</select>
</div>
<button type="submit" class="btn btn-outline-secondary btn-sm">
<i class="icon-base ti tabler-filter me-1"></i>
Filter
</button>
<button type="button" id="btn-reset-dashboard-filter" class="btn btn-outline-secondary btn-sm">
<i class="icon-base ti tabler-refresh me-1"></i>
Reset
</button>
</div>
</div>
</div>
</form>
</div>
<div class="row g-4 mb-4">
@foreach ($priceCards as $priceCard)
<div class="col-12 col-md-6 col-xl-3">
<div class="card card-border-shadow-{{ $priceCard['variant'] }} h-100">
<div class="card-body">
<div class="d-flex flex-column align-items-start gap-3">
<div class="avatar avatar-lg">
<span class="avatar-initial rounded bg-label-{{ $priceCard['variant'] }}">
<i class="icon-base ti {{ $priceCard['icon'] }} icon-28px"></i>
</span>
</div>
<div>
<div class="d-flex align-items-baseline gap-2 flex-wrap">
<h4 class="mb-0 pasar-kpi-value">{{ $priceCard['value'] }}</h4>
<small class="text-body-secondary">{{ $priceCard['unit'] }}</small>
</div>
<p class="mb-0 mt-1 pasar-kpi-label">{{ $priceCard['label'] }}</p>
<small class="text-body-secondary pasar-kpi-desc">{{ $priceCard['description'] }}</small>
</div>
</div>
<div class="mt-4 pt-3 border-top d-flex align-items-center justify-content-between gap-2 flex-wrap">
<span class="badge bg-label-{{ $priceCard['variant'] }}">{{ $priceCard['records'] > 0 ? 'Rata-rata harga' : 'Belum ada data' }}</span>
<small class="text-body-secondary">{{ number_format($priceCard['records'], 0, ',', '.') }} data harga</small>
</div>
</div>
</div>
</div>
@endforeach
</div>
<div class="card chart-group-header-toggle mb-4" role="button" tabindex="0"
data-bs-toggle="collapse" data-bs-target="#saluran-harga-charts"
aria-expanded="false" aria-controls="saluran-harga-charts" aria-label="Tampilkan chart">
<div class="card-body d-flex justify-content-between align-items-center gap-3">
<div>
<h5>Saluran &amp; Harga</h5>
<p class="mb-0 text-body-secondary">Saluran penjualan, hubungan dengan pembeli, waktu penjualan, akses ke saluran alternatif, dan tren harga.</p>
</div>
<span class="btn btn-icon btn-label-secondary btn-sm chart-group-icon-toggle" aria-hidden="true">
<i class="icon-base ti tabler-chevron-down"></i>
</span>
</div>
</div>
@php
$chartDefinitions = [
'saluranPenjualan' => ['title' => 'Saluran Penjualan Utama', 'description' => 'Kepada siapa petani biasanya menjual hasil panen?', 'variant' => 'success'],
'hubunganPembeli' => ['title' => 'Sifat Hubungan dengan Pembeli', 'description' => 'Hubungan jual petani dengan pembeli utama saat ini.', 'variant' => 'primary'],
'waktuPenjualan' => ['title' => 'Waktu Penjualan Hasil Panen', 'description' => 'Kapan petani biasanya menjual hasil panen?', 'variant' => 'warning'],
'aksesSaluranLain' => ['title' => 'Pengalaman & Akses ke Saluran Lain', 'description' => 'Pengalaman menjual ke tempat lain dan akses yang masih dimiliki.', 'variant' => 'info'],
];
@endphp
<div class="collapse chart-group-panel" id="saluran-harga-charts">
<div class="row g-4 mb-4">
@foreach ($chartDefinitions as $key => $definition)
<div class="col-12 col-lg-6">
<div class="card pasar-chart-card h-100">
<div class="card-header d-flex align-items-start justify-content-between gap-2">
<div class="card-title mb-0">
<h5 class="m-0 me-2" id="title-{{ $key }}">{{ $definition['title'] }}</h5>
<small class="text-body-secondary">{{ $definition['description'] }}</small>
</div>
</div>
<div class="card-body d-flex flex-column">
<div id="chart-{{ $key }}" class="pasar-chart" role="img" aria-labelledby="title-{{ $key }}" aria-describedby="summary-{{ $key }}"></div>
<span id="summary-{{ $key }}" class="visually-hidden">
@foreach ($marketCharts[$key]['labels'] as $index => $label)
{{ $label }}: {{ $marketCharts[$key]['series'][$index] }} responden ({{ $marketCharts[$key]['percentages'][$index] }}%).
@endforeach
</span>
</div>
</div>
</div>
@endforeach
@forelse ($priceTrend['commodities'] as $commodity)
<div class="col-12 col-lg-6">
<div class="card pasar-chart-card h-100">
<div class="card-header">
<div class="card-title mb-0">
<h5 class="m-0 me-2" id="title-tren-harga-{{ $commodity['id'] }}">Tren Harga 3 Musim — {{ $commodity['name'] }}</h5>
<small class="text-body-secondary">Rata-rata harga jual per bentuk produk selama tiga periode panen (Rp/kg).</small>
</div>
</div>
<div class="card-body">
<div id="chart-tren-harga-{{ $commodity['id'] }}" class="pasar-chart" role="img" aria-labelledby="title-tren-harga-{{ $commodity['id'] }}" aria-describedby="summary-tren-harga-{{ $commodity['id'] }}"></div>
<span id="summary-tren-harga-{{ $commodity['id'] }}" class="visually-hidden"></span>
</div>
</div>
</div>
@empty
<div class="col-12">
<div class="card pasar-chart-card">
<div class="card-header">
<div class="card-title mb-0"><h5 class="m-0">Tren Harga 3 Musim</h5></div>
</div>
<div class="card-body pasar-chart-empty">Belum ada komoditas untuk filter yang dipilih.</div>
</div>
</div>
@endforelse
</div>
</div>
<div class="card chart-group-header-toggle mb-4" role="button" tabindex="0"
data-bs-toggle="collapse" data-bs-target="#kesiapan-premium-charts"
aria-expanded="false" aria-controls="kesiapan-premium-charts" aria-label="Tampilkan chart">
<div class="card-body d-flex justify-content-between align-items-center gap-3">
<div>
<h5>Kesiapan Pasar Premium</h5>
<p class="mb-0 text-body-secondary">Kesadaran petani tentang pasar premium, persyaratan masuk, dan pengalaman mengaksesnya.</p>
</div>
<span class="btn btn-icon btn-label-secondary btn-sm chart-group-icon-toggle" aria-hidden="true">
<i class="icon-base ti tabler-chevron-down"></i>
</span>
</div>
</div>
<div class="collapse chart-group-panel" id="kesiapan-premium-charts">
<div class="row g-4 mb-4">
<div class="col-12">
<div class="card pasar-chart-card">
<div class="card-header">
<div class="card-title mb-0">
<h5 class="m-0 me-2" id="title-kesadaranPremium">Kesadaran Pasar Premium</h5>
<small class="text-body-secondary">Pengetahuan tentang pasar premium dan persyaratan yang dibutuhkan untuk masuk ke pasar tersebut.</small>
</div>
</div>
<div class="card-body">
<div id="chart-kesadaranPremium" class="pasar-chart" role="img" aria-labelledby="title-kesadaranPremium" aria-describedby="summary-kesadaranPremium"></div>
<span id="summary-kesadaranPremium" class="visually-hidden">
@foreach ($premiumAwareness['fullLabels'] as $index => $label)
{{ $label }}: {{ $premiumAwareness['series'][$index] }} responden ({{ $premiumAwareness['percentages'][$index] }}%).
@endforeach
</span>
</div>
</div>
</div>
</div>
</div>
<section class="mt-4" aria-labelledby="international-market-title" id="international-market">
<div class="d-flex align-items-center justify-content-between gap-3 flex-wrap mb-2">
<h5 id="international-market-title" class="mb-0">Harga Komoditas Internasional</h5>
<div class="d-flex gap-2">
<button type="button" class="btn btn-sm btn-outline-secondary" data-market-prev aria-label="Harga sebelumnya">&larr;</button>
<button type="button" class="btn btn-sm btn-icon btn-outline-secondary" data-market-pause aria-pressed="false" aria-label="Jeda slider" title="Jeda slider"><i class="icon-base ti tabler-player-pause" aria-hidden="true"></i></button>
<button type="button" class="btn btn-sm btn-outline-secondary" data-market-next aria-label="Harga berikutnya">&rarr;</button>
</div>
</div>
<p class="text-body-secondary small mb-2">Setiap kartu menampilkan satu komoditas dari satu sumber. Periode dan jenis harga mengikuti penyedia; tidak mengikuti filter wilayah.</p>
<p class="small text-body-secondary" data-market-status role="status">Memuat harga internasional...</p>
<div class="international-market-track" tabindex="0" role="region" aria-roledescription="carousel" aria-label="Harga komoditas menurut sumber"></div>
<template id="international-market-card">
<article class="card card-border-shadow-info international-market-card">
<div class="card-body">
<div class="d-flex justify-content-between align-items-start gap-3 mb-3">
<div class="flex-grow-1">
<div class="d-flex align-items-center gap-2 mb-2">
<h6 class="mb-0" data-name></h6>
<button type="button" class="market-info-button" data-market-info data-update-info aria-label="Informasi pembaruan harga" aria-expanded="false">!</button>
</div>
<div class="small">Sumber: <a data-source target="_blank" rel="noopener noreferrer"></a></div>
</div>
<span class="international-provider-logo flex-shrink-0" data-provider-logo-wrap hidden>
<img data-provider-logo width="32" height="32" alt="" decoding="async">
</span>
</div>
<div class="pasar-kpi-value" data-price></div>
<div class="small text-body-secondary mt-1" data-unit></div>
<div class="d-flex align-items-center gap-2 mt-3 pt-3 border-top">
<div class="fw-semibold fs-5" data-rupiah></div>
<button type="button" class="market-info-button" data-market-info data-fx-info aria-label="Informasi kurs konversi" aria-expanded="false">!</button>
</div>
<p class="small text-body-secondary mt-2 mb-2" data-description></p>
<div class="small text-body-secondary" data-period></div>
</div>
</article>
</template>
</section>
</div>
@endsection
@push('scripts')
<script>
document.addEventListener('DOMContentLoaded', function () {
const marketSection = document.getElementById('international-market');
const marketTrack = marketSection.querySelector('.international-market-track');
const marketStatus = marketSection.querySelector('[data-market-status]');
const pauseButton = marketSection.querySelector('[data-market-pause]');
let activeMarketInfo = null;
function closeMarketInfo() {
if (!activeMarketInfo) return;
activeMarketInfo.button.setAttribute('aria-expanded', 'false');
activeMarketInfo.popover.dispose();
activeMarketInfo = null;
}
document.addEventListener('click', event => {
const button = event.target.closest('[data-market-info]');
if (button && marketSection.contains(button)) {
const same = activeMarketInfo?.button === button;
closeMarketInfo();
if (same) return;
const popover = new bootstrap.Popover(button, {
container: 'body', placement: 'auto', trigger: 'manual', html: false,
title: button.hasAttribute('data-fx-info') ? 'Kurs konversi' : 'Pembaruan harga',
content: button.dataset.info || 'Informasi belum tersedia.'
});
popover.show();
button.setAttribute('aria-expanded', 'true');
activeMarketInfo = { button, popover };
} else if (!event.target.closest('.popover')) closeMarketInfo();
});
document.addEventListener('keydown', event => {
if (event.key === 'Escape') closeMarketInfo();
});
marketTrack.addEventListener('focusout', event => {
if (activeMarketInfo?.button === event.target) closeMarketInfo();
});
const reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)');
let marketPaused = reducedMotion.matches;
let marketSignature = '';
const quoteDateFormat = new Intl.DateTimeFormat('id-ID', {
dateStyle: 'medium', timeStyle: 'long', timeZone: 'Asia/Jakarta'
});
const rupiahFormat = new Intl.NumberFormat('id-ID', {
style: 'currency', currency: 'IDR', maximumFractionDigits: 2
});
function syncMarketPause() {
const label = marketPaused ? 'Putar slider' : 'Jeda slider';
pauseButton.querySelector('i').className = 'icon-base ti ' + (marketPaused ? 'tabler-player-play' : 'tabler-player-pause');
pauseButton.setAttribute('aria-label', label);
pauseButton.title = label;
pauseButton.setAttribute('aria-pressed', String(marketPaused));
}
syncMarketPause();
pauseButton.addEventListener('click', () => { marketPaused = !marketPaused; syncMarketPause(); });
let marketCycleWidth = 0;
let lastMarketFrame = null;
function measureMarketCycle() {
const first = marketTrack.firstElementChild;
const duplicate = marketTrack.querySelector('[data-market-clone]');
marketCycleWidth = first && duplicate ? duplicate.offsetLeft - first.offsetLeft : 0;
}
function slideMarket(direction) {
if (!marketTrack.firstElementChild) return;
const step = marketTrack.firstElementChild.getBoundingClientRect().width + 20;
if (direction < 0 && marketTrack.scrollLeft < step && marketCycleWidth) {
marketTrack.scrollLeft += marketCycleWidth;
}
marketTrack.scrollLeft += step * direction;
}
marketSection.querySelector('[data-market-prev]').addEventListener('click', () => slideMarket(-1));
marketSection.querySelector('[data-market-next]').addEventListener('click', () => slideMarket(1));
window.addEventListener('resize', measureMarketCycle);
function animateMarket(timestamp) {
const elapsed = lastMarketFrame === null ? 0 : Math.min(timestamp - lastMarketFrame, 64);
lastMarketFrame = timestamp;
if (marketCycleWidth > 0) {
if (!marketPaused && !document.hidden && !marketTrack.contains(document.activeElement)) {
// Keep subpixel progress independently: scrollLeft may round tiny per-frame changes.
marketScrollPosition += elapsed * 0.028;
marketTrack.scrollLeft = marketScrollPosition;
} else {
marketScrollPosition = marketTrack.scrollLeft;
}
if (marketTrack.scrollLeft >= marketCycleWidth) {
marketScrollPosition = marketTrack.scrollLeft - marketCycleWidth;
marketTrack.scrollLeft = marketScrollPosition;
}
}
window.requestAnimationFrame(animateMarket);
}
let marketScrollPosition = 0;
marketTrack.addEventListener('scroll', () => {
if (Math.abs(marketTrack.scrollLeft - marketScrollPosition) > 2) marketScrollPosition = marketTrack.scrollLeft;
}, { passive: true });
window.requestAnimationFrame(animateMarket);
async function refreshInternationalPrices() {
try {
const response = await fetch(@json(route('dashboard.pasar.international')), {
headers: { 'Accept': 'application/json' }, credentials: 'same-origin',
signal: AbortSignal.timeout(25000)
});
if (!response.ok) throw new Error('Harga tidak tersedia');
const payload = await response.json();
if (!Array.isArray(payload.quotes)) throw new Error('Data tidak valid');
const quotes = payload.quotes.filter(quote => typeof quote.price === 'number' && Number.isFinite(quote.price) && quote.price > 0);
const signature = JSON.stringify([quotes, payload.exchange_rate]);
if (signature !== marketSignature) {
const previousScroll = marketTrack.scrollLeft;
const fragment = document.createDocumentFragment();
quotes.forEach(quote => {
const card = document.getElementById('international-market-card').content.cloneNode(true);
card.querySelector('[data-name]').textContent = quote.name;
card.querySelector('[data-update-info]').dataset.info = quote.update_info || (quote.frequency === 'monthly'
? 'IMF menyediakan harga acuan rata-rata bulanan. Data terbaru diperiksa setiap hari. Periode harga tercantum di kartu.'
: 'Harga Yahoo Finance diperiksa setiap 5 menit. Data dapat tertunda mengikuti penyedia dan jam perdagangan.');
const rate = payload.exchange_rate;
let rateInfo = 'Kurs USD/IDR belum tersedia. Harga asli tetap ditampilkan.';
if (rate && typeof rate.value === 'number') {
rateInfo = '1 USD = ' + rupiahFormat.format(rate.value) + '. Sumber: ' + rate.source + '.';
const rateDate = new Date(rate.quoted_at);
if (!Number.isNaN(rateDate.getTime())) rateInfo += ' Waktu kurs: ' + quoteDateFormat.format(rateDate) + '.';
if (rate.stale) rateInfo += ' Menggunakan kurs tersimpan.';
if (quote.frequency === 'monthly') rateInfo += ' Konversi memakai kurs ini, bukan kurs historis bulan harga tersebut.';
}
card.querySelector('[data-fx-info]').dataset.info = rateInfo;
const providerLogos = {
yahoo: @json(asset('assets/img/providers/yahoo-finance.ico')),
imf: @json(asset('assets/img/providers/imf.ico')),
alpha: @json(asset('assets/img/providers/alpha-vantage.ico'))
};
const logoUrl = providerLogos[(quote.id || '').split(':')[0]];
if (logoUrl) {
const logo = card.querySelector('[data-provider-logo]');
logo.src = logoUrl;
logo.alt = 'Logo ' + quote.source;
card.querySelector('[data-provider-logo-wrap]').hidden = false;
}
const source = card.querySelector('[data-source]');
source.textContent = quote.source;
if (quote.source_url?.startsWith('https://')) source.href = quote.source_url;
card.querySelector('[data-price]').textContent = quote.price.toLocaleString('id-ID', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
card.querySelector('[data-unit]').textContent = quote.unit;
card.querySelector('[data-rupiah]').textContent = typeof quote.price_idr_per_kg === 'number'
? rupiahFormat.format(quote.price_idr_per_kg) + ' / kg' : 'Konversi rupiah belum tersedia';
card.querySelector('[data-description]').textContent = quote.description;
let period = '-';
if (quote.frequency === 'monthly' && /^\d{4}-\d{2}$/.test(quote.period || '')) {
period = 'Rata-rata ' + new Intl.DateTimeFormat('id-ID', { month: 'long', year: 'numeric', timeZone: 'UTC' }).format(new Date(quote.period + '-01T00:00:00Z'));
} else if (quote.period) {
const date = new Date(quote.period);
if (!Number.isNaN(date.getTime())) period = 'Waktu harga: ' + quoteDateFormat.format(date);
}
card.querySelector('[data-period]').textContent = period + (quote.stale ? ' · Data tersimpan' : '');
fragment.append(card);
});
closeMarketInfo();
marketTrack.replaceChildren(fragment);
if (quotes.length > 1) {
// Repeat the cards so the end joins the start without a visible rewind.
const originals = [...marketTrack.children];
[...originals, ...originals].forEach(original => {
const clone = original.cloneNode(true);
clone.dataset.marketClone = 'true';
marketTrack.append(clone);
});
}
measureMarketCycle();
marketScrollPosition = marketCycleWidth ? previousScroll % marketCycleWidth : 0;
marketTrack.scrollLeft = marketScrollPosition;
marketSignature = signature;
}
marketStatus.textContent = quotes.length
? '' : 'Harga belum tersedia. Pembaruan akan dicoba kembali otomatis.';
marketStatus.hidden = quotes.length > 0;
[pauseButton, ...marketSection.querySelectorAll('[data-market-prev], [data-market-next]')].forEach(button => button.disabled = quotes.length < 2);
} catch (error) {
marketStatus.hidden = false;
marketStatus.textContent = marketTrack.childElementCount
? 'Pembaruan gagal. Menampilkan harga sebelumnya dan akan mencoba kembali.'
: 'Harga belum dapat dimuat. Akan dicoba kembali otomatis.';
} finally {
window.setTimeout(() => {
if (!document.hidden) refreshInternationalPrices();
else waitForVisible();
}, 60000);
}
}
function waitForVisible() {
document.addEventListener('visibilitychange', function onVisible() {
if (document.hidden) return;
document.removeEventListener('visibilitychange', onVisible);
refreshInternationalPrices();
});
}
refreshInternationalPrices();
const wilayah = @json($wilayah);
const marketCharts = @json($marketCharts);
const chartDefinitions = @json($chartDefinitions);
const priceTrend = @json($priceTrend);
const premiumAwareness = @json($premiumAwareness);
function escapeHtml(value) {
const element = document.createElement('span');
element.textContent = String(value);
return element.innerHTML;
}
function formatNumber(value, digits = 0) {
return Number(value).toLocaleString('id-ID', { maximumFractionDigits: digits });
}
const compactLabels = {
saluranPenjualan: ['Tengkulak/pedagang lokal', 'Koperasi', 'BUMDes', 'Pembeli langsung/eksportir', 'Lainnya'],
waktuPenjualan: ['Langsung setelah panen', 'Ditumpuk beberapa waktu', 'Saat butuh uang mendesak'],
aksesSaluranLain: ['Tidak pernah', 'Berhenti, tanpa akses', 'Berhenti, bisa kembali', 'Sesekali masih menjual', 'Akses alternatif aktif'],
kesadaranPremium: ['Belum pernah mendengar', 'Belum paham persyaratan', 'Sedikit paham persyaratan', 'Paham, belum mengakses', 'Akses premium konsisten'],
};
const mobileLabels = {
saluranPenjualan: ['Tengkulak/lokal', 'Koperasi', 'BUMDes', 'Pembeli/eksportir', 'Lainnya'],
hubunganPembeli: ['Bebas pindah', 'Langganan tetap', 'Berbasis hutang', 'Titip jual', 'Komitmen volume', 'Semi-kontrak'],
waktuPenjualan: ['Setelah panen', 'Ditumpuk dulu', 'Uang mendesak'],
aksesSaluranLain: ['Tidak pernah', 'Tanpa akses', 'Bisa kembali', 'Sesekali jual', 'Akses aktif'],
kesadaranPremium: ['Belum mendengar', 'Belum paham syarat', 'Sedikit paham', 'Paham, belum akses', 'Akses konsisten'],
};
function hasTrendData(commodity) {
return commodity && commodity.series.some(function (series) {
return series.data.some(function (value) { return value !== null && Number(value) > 0; });
});
}
function formatPrice(value) {
return value === null || value === undefined ? 'Belum ada data' : 'Rp ' + formatNumber(value) + '/kg';
}
function renderPriceTrend(commodity) {
const element = document.getElementById('chart-tren-harga-' + commodity.id);
const summary = document.getElementById('summary-tren-harga-' + commodity.id);
if (!hasTrendData(commodity) || typeof ApexCharts === 'undefined') {
element.classList.add('pasar-chart-empty');
element.textContent = !hasTrendData(commodity)
? 'Belum ada data harga untuk komoditas dan filter yang dipilih.'
: 'Chart belum dapat dimuat. Silakan muat ulang halaman.';
return;
}
summary.textContent = commodity.name + '. ' + commodity.series.map(function (series) {
return series.name + ': ' + series.data.map(function (value, index) {
return priceTrend.periods[index] + ' ' + formatPrice(value);
}).join(', ');
}).join('. ');
const colors = config.chartColors.series.slice(0, 4);
const labelStyle = { colors: config.colors.bodyColor, fontSize: '12px', fontFamily: config.fontFamily };
new ApexCharts(element, {
chart: { type: 'line', height: 360, fontFamily: config.fontFamily, foreColor: config.colors.bodyColor, toolbar: { show: false }, zoom: { enabled: false } },
series: commodity.series,
colors: colors,
stroke: { width: 3, curve: 'straight' },
markers: { size: 5, strokeWidth: 2, hover: { sizeOffset: 2 } },
dataLabels: { enabled: false },
xaxis: {
categories: priceTrend.periods,
tickPlacement: 'between',
labels: { rotate: 0, style: labelStyle },
axisBorder: { color: config.chartColors.grid },
axisTicks: { color: config.chartColors.grid },
},
yaxis: {
min: 0,
title: { text: 'Harga (Rp/kg)', style: { color: config.colors.bodyColor, fontFamily: config.fontFamily, fontWeight: 400 } },
labels: { style: labelStyle, formatter: function (value) { return formatNumber(value); } },
},
grid: { borderColor: config.chartColors.grid, strokeDashArray: 4, padding: { left: 8, right: 20 } },
legend: { position: 'bottom', fontSize: '13px', fontFamily: config.fontFamily, labels: { colors: config.colors.bodyColor }, itemMargin: { horizontal: 12, vertical: 6 } },
tooltip: {
shared: true,
intersect: false,
custom: function ({ dataPointIndex }) {
const rows = commodity.series.map(function (series, index) {
return `<div class="pasar-trend-tooltip-row"><span style="color:${colors[index]}">${escapeHtml(series.name)}</span><strong>${formatPrice(series.data[dataPointIndex])}</strong></div>`;
}).join('');
return `<div class="pasar-chart-tooltip" style="background:${config.colors.cardColor};color:${config.colors.headingColor}"><strong>${escapeHtml(commodity.name)} — ${escapeHtml(priceTrend.periods[dataPointIndex])}</strong>${rows}</div>`;
},
},
responsive: [{
breakpoint: 576,
options: {
chart: { height: 420 },
legend: { fontSize: '12px', itemMargin: { horizontal: 6, vertical: 4 } },
xaxis: {
labels: {
hideOverlappingLabels: false,
style: { fontSize: '11px' },
formatter: function (value) {
return { 'Dua Tahun Lalu': '2 thn lalu', 'Tahun Lalu': 'Tahun lalu', 'Panen Terakhir': 'Terakhir' }[value] || value;
},
},
},
yaxis: {
title: { text: undefined },
labels: { style: { fontSize: '11px' }, formatter: function (value) { return formatNumber(value); } },
},
},
}],
}).render();
}
function renderResponseChart(key, data, definition) {
const element = document.getElementById('chart-' + key);
if (!data.respondents || typeof ApexCharts === 'undefined') {
element.classList.add('pasar-chart-empty');
element.textContent = !data.respondents
? 'Belum ada data jawaban untuk filter yang dipilih.'
: 'Chart belum dapat dimuat. Silakan muat ulang halaman.';
return;
}
const color = config.chartColors.semantic[definition.variant];
const borderColor = config.chartColors.grid;
const labelStyle = { colors: config.colors.bodyColor, fontSize: '12px', fontFamily: config.fontFamily };
const maxCount = Math.max(...data.series);
const tickCount = Math.min(4, maxCount);
const axisMax = Math.ceil(maxCount * 1.25 / tickCount) * tickCount;
new ApexCharts(element, {
chart: { type: 'bar', height: 330, fontFamily: config.fontFamily, foreColor: config.colors.bodyColor, toolbar: { show: false } },
series: [{ name: 'Responden', data: data.series }],
colors: [color],
plotOptions: { bar: { horizontal: true, borderRadius: 5, barHeight: '62%', dataLabels: { position: 'top' } } },
dataLabels: {
enabled: true,
textAnchor: 'start',
offsetX: 6,
style: { fontSize: '12px', fontFamily: config.fontFamily, fontWeight: 500, colors: [config.colors.headingColor] },
formatter: function (value, options) {
return `${formatNumber(value)} (${formatNumber(data.percentages[options.dataPointIndex], 1)}%)`;
},
},
xaxis: {
categories: compactLabels[key] || data.labels,
min: 0,
max: axisMax,
tickAmount: tickCount,
labels: { style: labelStyle, formatter: function (value) { return formatNumber(value); } },
axisBorder: { color: borderColor },
axisTicks: { color: borderColor },
},
yaxis: { labels: { maxWidth: 185, style: labelStyle } },
grid: { borderColor: borderColor, strokeDashArray: 4, padding: { right: 20 } },
legend: { show: false },
tooltip: {
theme: document.documentElement.getAttribute('data-bs-theme') === 'dark' ? 'dark' : 'light',
custom: function ({ dataPointIndex }) {
return `<div class="pasar-chart-tooltip" style="background:${color};color:${config.colors.headingColor};font-family:${config.fontFamily}">${escapeHtml(data.fullLabels[dataPointIndex])}: <strong>${formatNumber(data.series[dataPointIndex])} responden (${formatNumber(data.percentages[dataPointIndex], 1)}%)</strong></div>`;
},
},
responsive: [{
breakpoint: 576,
options: {
xaxis: { categories: mobileLabels[key], tickAmount: 2 },
yaxis: { labels: { maxWidth: 125, style: { fontSize: '11px' } } },
grid: { padding: { right: 10 } },
dataLabels: { style: { fontSize: '11px' }, formatter: function (value) { return formatNumber(value); } },
},
}],
}).render();
}
function renderMarketCharts() {
Object.entries(marketCharts).forEach(function ([key, data]) {
renderResponseChart(key, data, chartDefinitions[key]);
});
priceTrend.commodities.forEach(renderPriceTrend);
}
function setupChartPanel(panelId, renderCharts) {
const chartPanel = document.getElementById(panelId);
const chartControl = document.querySelector('.chart-group-header-toggle[aria-controls="' + chartPanel.id + '"]');
const chartIcon = chartControl.querySelector('.chart-group-icon-toggle i');
let rendered = false;
function syncChartPanelState() {
const expanded = chartPanel.classList.contains('show');
const label = expanded ? 'Sembunyikan chart' : 'Tampilkan chart';
chartControl.setAttribute('aria-expanded', String(expanded));
chartControl.setAttribute('aria-label', label);
chartControl.title = label;
chartIcon.classList.toggle('tabler-chevron-up', expanded);
chartIcon.classList.toggle('tabler-chevron-down', !expanded);
}
chartControl.addEventListener('keydown', function (event) {
if (event.target !== chartControl || !['Enter', ' '].includes(event.key)) return;
event.preventDefault();
chartControl.click();
});
chartPanel.addEventListener('shown.bs.collapse', function (event) {
if (event.target !== chartPanel) return;
syncChartPanelState();
if (!rendered) {
renderCharts();
rendered = true;
}
window.dispatchEvent(new Event('resize'));
});
chartPanel.addEventListener('hidden.bs.collapse', function (event) {
if (event.target === chartPanel) syncChartPanelState();
});
syncChartPanelState();
}
setupChartPanel('saluran-harga-charts', renderMarketCharts);
setupChartPanel('kesiapan-premium-charts', function () {
renderResponseChart('kesadaranPremium', premiumAwareness, { variant: 'primary' });
});
function fillSelect($select, items, placeholder) {
$select.empty().append(new Option(placeholder, ''));
items.forEach(function (item) {
$select.append(new Option(item.nama, item.id));
});
}
function selectedProvinsi() {
const id = $('#filter-provinsi').val();
return wilayah.find(function (item) {
return String(item.id) === String(id);
});
}
function selectedKabupaten() {
const provinsi = selectedProvinsi();
const id = $('#filter-kabkota').val();
return (provinsi?.kabupaten || []).find(function (item) {
return String(item.id) === String(id);
});
}
function selectedKecamatan() {
const kabupaten = selectedKabupaten();
const id = $('#filter-kecamatan').val();
return (kabupaten?.kecamatan || []).find(function (item) {
return String(item.id) === String(id);
});
}
$('#filter-provinsi, #filter-kabkota, #filter-kecamatan, #filter-desa, #filter-komoditas, #filter-segmen').select2({ width: '100%' });
$('#filter-provinsi').on('change', function () {
const provinsi = selectedProvinsi();
fillSelect($('#filter-kabkota'), provinsi?.kabupaten || [], 'Semua Kabupaten/Kota');
fillSelect($('#filter-kecamatan'), [], 'Semua Kecamatan');
fillSelect($('#filter-desa'), [], 'Semua Desa/Kelurahan');
$('#filter-kabkota').prop('disabled', !provinsi).trigger('change.select2');
$('#filter-kecamatan, #filter-desa').prop('disabled', true).trigger('change.select2');
});
$('#filter-kabkota').on('change', function () {
const kabupaten = selectedKabupaten();
fillSelect($('#filter-kecamatan'), kabupaten?.kecamatan || [], 'Semua Kecamatan');
fillSelect($('#filter-desa'), [], 'Semua Desa/Kelurahan');
$('#filter-kecamatan').prop('disabled', !kabupaten).trigger('change.select2');
$('#filter-desa').prop('disabled', true).trigger('change.select2');
});
$('#filter-kecamatan').on('change', function () {
const kecamatan = selectedKecamatan();
fillSelect($('#filter-desa'), kecamatan?.desa || [], 'Semua Desa/Kelurahan');
$('#filter-desa').prop('disabled', !kecamatan).trigger('change.select2');
});
const defaultProvinsi = String($('#filter-provinsi').data('default') || '');
const defaultKabupaten = String($('#filter-kabkota').data('default') || '');
const defaultKecamatan = String($('#filter-kecamatan').data('default') || '');
const defaultDesa = String($('#filter-desa').data('default') || '');
if (defaultProvinsi) {
$('#filter-provinsi').val(defaultProvinsi).trigger('change');
if (defaultKabupaten) {
$('#filter-kabkota').val(defaultKabupaten).trigger('change');
if (defaultKecamatan) {
$('#filter-kecamatan').val(defaultKecamatan).trigger('change');
if (defaultDesa) {
$('#filter-desa').val(defaultDesa).trigger('change.select2');
}
}
}
}
$('#btn-reset-dashboard-filter').on('click', function () {
window.location.href = @json(route('dashboard.pasar'));
});
});
</script>
@endpush
+3 -2
View File
@@ -493,6 +493,7 @@ $segments = [
legendColor = config.colors.bodyColor;
borderColor = config.colors.borderColor;
headingColor = config.colors.headingColor;
const pastelSemantic = config.chartColors?.semantic || {};
var radarDimensi = new ApexCharts($("#chart-dimensi")[0],
options = {
@@ -542,7 +543,7 @@ $segments = [
highlightDataSeries: false
}
},
colors: [config.colors.primary, config.colors.info],
colors: [pastelSemantic.primary || '#9D8EF0', pastelSemantic.info || '#5FC7D6'],
fill: {
opacity: 0.85
},
@@ -640,7 +641,7 @@ $segments = [
highlightDataSeries: false
}
},
colors: [config.colors.primary, config.colors.info],
colors: [pastelSemantic.primary || '#9D8EF0', pastelSemantic.info || '#5FC7D6'],
fill: {
opacity: 0.85
},
File diff suppressed because it is too large Load Diff
+27 -24
View File
@@ -144,10 +144,10 @@
<div class="absolute bottom-3 left-3 bg-white/95 p-3 rounded-lg text-xs border border-gray-200 shadow-sm backdrop-blur-sm">
<div class="font-bold text-gray-700 mb-2">Legenda Tanaman:</div>
<div class="grid grid-cols-2 gap-x-4 gap-y-2">
<div class="flex items-center gap-2"><div class="w-3 h-3 bg-[#6F4E37] rounded-full shadow-sm"></div> Kopi</div>
<div class="flex items-center gap-2"><div class="w-3 h-3 bg-[#eab308] rounded-full shadow-sm"></div> Vanili</div>
<div class="flex items-center gap-2"><div class="w-3 h-3 bg-[#D2691E] rounded-full shadow-sm"></div> Kakao</div>
<div class="flex items-center gap-2"><div class="w-3 h-3 bg-[#9ca3af] rounded-full shadow-sm"></div> Kemiri</div>
<div class="flex items-center gap-2"><div class="w-3 h-3 bg-[#6FD68F] rounded-full shadow-sm"></div> Kopi</div>
<div class="flex items-center gap-2"><div class="w-3 h-3 bg-[#FFB46F] rounded-full shadow-sm"></div> Vanili</div>
<div class="flex items-center gap-2"><div class="w-3 h-3 bg-[#9D8EF0] rounded-full shadow-sm"></div> Kakao</div>
<div class="flex items-center gap-2"><div class="w-3 h-3 bg-[#9EA6B0] rounded-full shadow-sm"></div> Kemiri</div>
</div>
</div>
</div>
@@ -253,6 +253,9 @@
@push('scripts')
<script>
document.addEventListener('DOMContentLoaded', function() {
const pastelChart = config.chartColors || {};
const pastelCategory = pastelChart.categories || {};
const gridColor = pastelChart.grid || '#E8EDF5';
// 1. Inisialisasi Icons
if(typeof lucide !== 'undefined') {
@@ -263,19 +266,19 @@ document.addEventListener('DOMContentLoaded', function() {
const spatialData = [
{
id: 'L-001', owner: 'Yosef Neta', familySize: 5, area: '1.2 Ha', village: 'Radabata',
polygon: "15,25 75,15 90,85 20,70", fill: "rgba(34, 197, 94, 0.15)", stroke: "#16a34a",
polygon: "15,25 75,15 90,85 20,70", fill: "rgba(111, 214, 143, 0.24)", stroke: "#6FD68F",
plants: [
{ id: 'P-01', type: 'Kopi', cx: 35, cy: 35, planted: 'Mei 2021', status: 'Produktif', intervensi: 'Pemupukan Organik (Okt 2023)', panen: '45 kg/musim', color: '#6F4E37' },
{ id: 'P-02', type: 'Vanili', cx: 65, cy: 45, planted: 'Jan 2022', status: 'Berbunga', intervensi: 'Pelatihan Polinasi (Feb 2024)', panen: 'Belum panen', color: '#eab308' },
{ id: 'P-03', type: 'Kopi', cx: 50, cy: 65, planted: 'Mei 2021', status: 'Produktif', intervensi: 'Pemangkasan (Nov 2023)', panen: '40 kg/musim', color: '#6F4E37' },
{ id: 'P-01', type: 'Kopi', cx: 35, cy: 35, planted: 'Mei 2021', status: 'Produktif', intervensi: 'Pemupukan Organik (Okt 2023)', panen: '45 kg/musim', color: pastelCategory.coffee || '#6FD68F' },
{ id: 'P-02', type: 'Vanili', cx: 65, cy: 45, planted: 'Jan 2022', status: 'Berbunga', intervensi: 'Pelatihan Polinasi (Feb 2024)', panen: 'Belum panen', color: pastelCategory.vanilla || '#FFB46F' },
{ id: 'P-03', type: 'Kopi', cx: 50, cy: 65, planted: 'Mei 2021', status: 'Produktif', intervensi: 'Pemangkasan (Nov 2023)', panen: '40 kg/musim', color: pastelCategory.coffee || '#6FD68F' },
]
},
{
id: 'L-002', owner: 'Maria Goreti', familySize: 3, area: '0.8 Ha', village: 'Ubedolumolo',
polygon: "110,40 170,15 190,65 125,85", fill: "rgba(59, 130, 246, 0.15)", stroke: "#2563eb",
polygon: "110,40 170,15 190,65 125,85", fill: "rgba(95, 199, 214, 0.20)", stroke: "#5FC7D6",
plants: [
{ id: 'P-04', type: 'Kemiri', cx: 140, cy: 45, planted: 'Agu 2018', status: 'Produktif', intervensi: 'Pembersihan Gulma', panen: '120 kg/musim', color: '#9ca3af' },
{ id: 'P-05', type: 'Kakao', cx: 165, cy: 55, planted: 'Okt 2020', status: 'Terserang Hama', intervensi: 'Penyemprotan Bio-pestisida (Jan 2024)', panen: '20 kg/musim', color: '#D2691E' },
{ id: 'P-04', type: 'Kemiri', cx: 140, cy: 45, planted: 'Agu 2018', status: 'Produktif', intervensi: 'Pembersihan Gulma', panen: '120 kg/musim', color: pastelCategory.candlenut || '#9EA6B0' },
{ id: 'P-05', type: 'Kakao', cx: 165, cy: 55, planted: 'Okt 2020', status: 'Terserang Hama', intervensi: 'Penyemprotan Bio-pestisida (Jan 2024)', panen: '20 kg/musim', color: pastelCategory.cacao || '#9D8EF0' },
]
}
];
@@ -440,24 +443,24 @@ document.addEventListener('DOMContentLoaded', function() {
{
label: 'Kopi',
data: [0.5, 0.7, 1.1, 1.5],
borderColor: '#6F4E37',
backgroundColor: '#6F4E37',
borderColor: pastelCategory.coffee || '#6FD68F',
backgroundColor: pastelCategory.coffee || '#6FD68F',
tension: 0.3,
borderWidth: 2
},
{
label: 'Vanili',
data: [0.1, 0.15, 0.3, 0.5],
borderColor: '#eab308',
backgroundColor: '#eab308',
borderColor: pastelCategory.vanilla || '#FFB46F',
backgroundColor: pastelCategory.vanilla || '#FFB46F',
tension: 0.3,
borderWidth: 2
},
{
label: 'Kakao',
data: [0.4, 0.5, 0.8, 1.2],
borderColor: '#D2691E',
backgroundColor: '#D2691E',
borderColor: pastelCategory.cacao || '#9D8EF0',
backgroundColor: pastelCategory.cacao || '#9D8EF0',
tension: 0.3,
borderWidth: 2
}
@@ -467,7 +470,7 @@ document.addEventListener('DOMContentLoaded', function() {
responsive: true, maintainAspectRatio: false,
interaction: { mode: 'index', intersect: false },
scales: {
y: { title: { display: true, text: 'Ton per Hektar (Ton/Ha)' }, grid: { color: '#f3f4f6' } },
y: { title: { display: true, text: 'Ton per Hektar (Ton/Ha)' }, grid: { color: gridColor } },
x: { grid: { display: false } }
},
plugins: {
@@ -489,11 +492,11 @@ document.addEventListener('DOMContentLoaded', function() {
label: 'Jumlah Bibit Didistribusikan',
data: [15000, 5200, 8000, 7000, 10000],
backgroundColor: [
'rgba(111, 78, 55, 0.8)', // Kopi
'rgba(234, 179, 8, 0.8)', // Vanili
'rgba(210, 105, 30, 0.8)', // Kakao
'rgba(156, 163, 175, 0.8)', // Kemiri
'rgba(34, 197, 94, 0.8)' // Pelindung (Hijau)
'rgba(111, 214, 143, 0.86)', // Kopi
'rgba(255, 180, 111, 0.86)', // Vanili
'rgba(157, 142, 240, 0.86)', // Kakao
'rgba(179, 181, 188, 0.86)', // Kemiri
'rgba(101, 216, 189, 0.86)' // Pelindung
],
borderRadius: 4
}
@@ -502,7 +505,7 @@ document.addEventListener('DOMContentLoaded', function() {
options: {
responsive: true, maintainAspectRatio: false,
scales: {
y: { beginAtZero: true, grid: { color: '#f3f4f6' } },
y: { beginAtZero: true, grid: { color: gridColor } },
x: { grid: { display: false } }
},
plugins: {
+15 -6
View File
@@ -219,6 +219,10 @@
@push('scripts')
<script>
document.addEventListener('DOMContentLoaded', function() {
const pastelChart = config.chartColors || {};
const pastelSemantic = pastelChart.semantic || {};
const pastelCategory = pastelChart.categories || {};
const gridColor = pastelChart.grid || '#E8EDF5';
// Inisialisasi Icons (Khusus Mockup, Vuexy mungkin pakai library icon sendiri seperti Tabler/FontAwesome)
if(typeof lucide !== 'undefined') {
@@ -236,7 +240,7 @@
{
label: 'Baseline (Tanpa Intervensi)',
data: [12, 12.5, 13, 13.5],
borderColor: '#9ca3af',
borderColor: pastelSemantic.gray || '#9EA6B0',
backgroundColor: 'transparent',
borderDash: [5, 5],
fill: false,
@@ -245,11 +249,11 @@
{
label: 'Pendapatan Aktual',
data: [12, 15, 22, 31],
borderColor: '#10b981',
backgroundColor: 'rgba(16, 185, 129, 0.15)', // Light green fill
borderColor: pastelCategory.marketGreen || '#6FD68F',
backgroundColor: 'rgba(111, 214, 143, 0.20)',
fill: true,
tension: 0.4,
pointBackgroundColor: '#10b981',
pointBackgroundColor: pastelCategory.marketGreen || '#6FD68F',
pointBorderWidth: 2,
pointRadius: 4
}
@@ -265,7 +269,7 @@
scales: {
y: {
beginAtZero: false,
grid: { color: '#f3f4f6' },
grid: { color: gridColor },
ticks: { callback: function(val) { return 'Rp ' + val + 'M'; } }
},
x: {
@@ -296,7 +300,12 @@
labels: ['Kopi', 'Vanili', 'Kakao', 'Kemiri'],
datasets: [{
data: [45, 25, 15, 15],
backgroundColor: ['#6F4E37', '#eab308', '#D2691E', '#9ca3af'],
backgroundColor: [
pastelCategory.coffee || '#6FD68F',
pastelCategory.vanilla || '#FFB46F',
pastelCategory.cacao || '#9D8EF0',
pastelCategory.candlenut || '#9EA6B0'
],
borderWidth: 2,
borderColor: '#ffffff',
hoverOffset: 4
@@ -12,9 +12,9 @@
box-shadow: 0 .25rem 1rem rgba(34, 48, 62, .08);
}
.tk-stat.green { border-left-color: #28a745; }
.tk-stat.blue { border-left-color: #007bff; }
.tk-stat.amber { border-left-color: #fd7e14; }
.tk-stat.green { border-left-color: #6FD68F; }
.tk-stat.blue { border-left-color: #5FC7D6; }
.tk-stat.amber { border-left-color: #FFB46F; }
.tk-stat-lbl {
font-size: 10px;
@@ -444,10 +444,10 @@
@forelse ($progressPetani as $petani)
@php
$barColor = match (true) {
$petani['percent'] === 100 => '#28a745',
$petani['percent'] >= 50 => '#007bff',
$petani['percent'] >= 20 => '#fd7e14',
default => '#dc3545',
$petani['percent'] === 100 => '#6FD68F',
$petani['percent'] >= 50 => '#5FC7D6',
$petani['percent'] >= 20 => '#FFB46F',
default => '#F47C86',
};
$initial = collect(explode(' ', trim($petani['name'])))
@@ -465,8 +465,8 @@
style="
width: 28px;
height: 28px;
background: rgba(40,167,69,.15);
color: #28a745;
background: rgba(111,214,143,.24);
color: #6FD68F;
"
>
{{ $initial ?: 'PT' }}
@@ -0,0 +1,306 @@
@extends('layout.main')
@section('content')
<div class="container-xxl flex-grow-1 container-p-y">
@include('koperasi.profil.header', ['pengurus' => $pengurus])
<div class="card shadow-sm border-0 mb-4">
<div class="card-body p-0">
<form action="{{ route('koperasi.pengurus.survey.store', ['id' => $pengurus->id ?? 0, 'step' => 1]) }}" method="POST" class="p-4 p-md-5">
@csrf
<input type="hidden" name="pengurus_koperasi_id" value="{{ $pengurus->id ?? '' }}">
<h5 class="text-muted fw-bold border-bottom pb-2 mb-2">KELEMBAGAAN & AKTOR SISTEM</h5>
<p class="text-muted small mb-3">Memetakan potensi dan kesiapan koperasi sebagai agregator atau mitra pengelola rantai nilai komoditas.</p>
<h5 class="text-muted pb-2 mt-7 mb-5">KOPERASI & BUMDes</h5>
<h5 class="pb-3 border-bottom mb-5 mt-7">Keberadaan Koperasi</h5>
<div class="mb-4">
<h6 class="form-label">Bagaimana sejarah awal pendirian koperasi/CU bapak/ibu? Apa tujuan/cita-cita bersama yang ingin dicapai saat itu?</h6>
<textarea class="form-control" name="sejarah_lembaga" rows="3" placeholder="Ceritakan sejarah singkat...">{{ old('sejarah_lembaga', $pengurus->sejarah_lembaga) }}</textarea>
</div>
<div class="row mb-4">
<div class="col-md-6 mb-3">
<h6 class="form-label">Apakah koperasi ini memiliki fokus pada pengembangan komoditas pertanian tertentu?</h6>
<div class="form-check mb-2">
<input class="form-check-input trigger-toggle" type="radio" name="fokus_komoditas" value="Tidak" id="fokusTidak" data-target="fokusYaInput" data-action="hide" {{ old('fokus_komoditas', $pengurus->fokus_komoditas) == 'Tidak' ? 'checked' : '' }}>
<label class="form-check-label" for="fokusTidak">Tidak</label>
</div>
<div class="form-check d-flex align-items-center gap-2">
<input class="form-check-input trigger-toggle" type="radio" name="fokus_komoditas" value="Ya" id="fokusYa" data-target="fokusYaInput" data-action="show" {{ old('fokus_komoditas', $pengurus->fokus_komoditas) == 'Ya' ? 'checked' : '' }}>
<label class="form-check-label text-nowrap" for="fokusYa">Ya, sebutkan:</label>
<input type="text" class="form-control form-control-sm w-75 {{ old('fokus_komoditas', $pengurus->fokus_komoditas) == 'Ya' ? '' : 'd-none' }}" id="fokusYaInput" name="detail_fokus_komoditas" placeholder="Contoh: Kopi, Vanili" value="{{ old('detail_fokus_komoditas', $pengurus->detail_fokus_komoditas) }}">
</div>
</div>
<div class="col-md-6 mb-3">
<h6 class="form-label">Apakah ada dokumen kelembagaan (AD/ART)?</h6>
<div class="form-check d-flex align-items-center gap-2 mb-2">
<input class="form-check-input trigger-toggle" type="radio" name="ad_art" value="Ada, lengkap" id="adArtAda" data-target="adArtTahun" data-action="show" {{ old('ad_art', $pengurus->ad_art) == 'Ada, lengkap' ? 'checked' : '' }}>
<label class="form-check-label text-nowrap" for="adArtAda">Ada, lengkap, terakhir diperbarui tahun:</label>
<input type="number" class="form-control form-control-sm w-25 {{ old('ad_art', $pengurus->ad_art) == 'Ada, lengkap' ? '' : 'd-none' }}" id="adArtTahun" name="tahun_ad_art" placeholder="2020" value="{{ old('tahun_ad_art', $pengurus->tahun_ad_art) }}">
</div>
<div class="form-check"><input class="form-check-input trigger-toggle" type="radio" name="ad_art" value="Ada tapi tidak lengkap" data-target="adArtTahun" data-action="hide" {{ old('ad_art', $pengurus->ad_art) == 'Ada tapi tidak lengkap' ? 'checked' : '' }}><label class="form-check-label">Ada tapi tidak lengkap</label></div>
<div class="form-check"><input class="form-check-input trigger-toggle" type="radio" name="ad_art" value="Tidak ada" data-target="adArtTahun" data-action="hide" {{ old('ad_art', $pengurus->ad_art) == 'Tidak ada' ? 'checked' : '' }}><label class="form-check-label">Tidak ada</label></div>
<div class="form-check"><input class="form-check-input trigger-toggle" type="radio" name="ad_art" value="Tidak tahu" data-target="adArtTahun" data-action="hide" {{ old('ad_art', $pengurus->ad_art) == 'Tidak tahu' ? 'checked' : '' }}><label class="form-check-label">Tidak tahu</label></div>
</div>
</div>
<h5 class="pb-3 border-bottom mb-10 mt-4">Ketersediaan Layanan</h5>
<div class="mb-4">
<h6 class="form-label">Apa saja layanan yang disediakan oleh koperasi bapak/ibu untuk anggota koperasi?</h6>
<div class="form-check"><input class="form-check-input" type="checkbox" name="layanan_koperasi[]" value="Simpan pinjam" id="cekSimpanPinjam" {{ str_contains($pengurus->layanan_koperasi ?? '', 'Simpan pinjam') ? 'checked' : '' }}><label class="form-check-label">Simpan pinjam</label></div>
<div class="form-check"><input class="form-check-input" type="checkbox" name="layanan_koperasi[]" value="Pengadaan input produksi" {{ str_contains($pengurus->layanan_koperasi ?? '', 'Pengadaan input produksi') ? 'checked' : '' }}><label class="form-check-label">Pengadaan input produksi</label></div>
<div class="form-check"><input class="form-check-input" type="checkbox" name="layanan_koperasi[]" value="Pemasaran hasil" {{ str_contains($pengurus->layanan_koperasi ?? '', 'Pemasaran hasil') ? 'checked' : '' }}><label class="form-check-label">Pemasaran hasil</label></div>
<div class="form-check d-flex align-items-center gap-2 mt-2">
<input class="form-check-input trigger-toggle-check" type="checkbox" name="layanan_koperasi[]" value="Lainnya" data-target="layananLainnya" {{ str_contains($pengurus->layanan_koperasi ?? '', 'Lainnya') ? 'checked' : '' }}>
<label class="form-check-label text-nowrap">Lainnya, sebutkan:</label>
<input type="text" class="form-control form-control-sm w-50 {{ str_contains($pengurus->layanan_koperasi ?? '', 'Lainnya') ? '' : 'd-none' }}" id="layananLainnya" name="layanan_lainnya" placeholder="..." value="{{ old('layanan_lainnya', $pengurus->layanan_lainnya) }}">
</div>
</div>
<div class="mb-10">
<h6 class="form-label">Apakah terdapat program/pelatihan untuk meningkatkan kapasitas anggota koperasi?</h6>
<div class="table-responsive mb-5">
<table class="table table-sm w-75">
<thead class="table-primary ">
<tr><th style="border-top-left-radius: 8px; border-bottom-left-radius: 8px; border: none;">Jenis Pelatihan</th><th class="text-center " style="width: 100px; border: none;">Ya</th><th class="text-center" style="border-top-right-radius: 8px; border-bottom-right-radius: 8px; width: 100px; border: none;">Tidak</th></tr>
</thead>
<tbody>
<tr><td>Pelatihan produksi pertanian</td><td class="text-center"><input class="form-check-input" type="radio" name="pelatihan_produksi" value="Ya" {{ old('pelatihan_produksi', $pengurus->pelatihan_produksi) == 'Ya' ? 'checked' : '' }}></td><td class="text-center"><input class="form-check-input" type="radio" name="pelatihan_produksi" value="Tidak" {{ old('pelatihan_produksi', $pengurus->pelatihan_produksi) == 'Tidak' ? 'checked' : '' }}></td></tr>
<tr><td>Pelatihan penanganan pascapanen</td><td class="text-center"><input class="form-check-input" type="radio" name="pelatihan_pascapanen" value="Ya" {{ old('pelatihan_pascapanen', $pengurus->pelatihan_pascapanen) == 'Ya' ? 'checked' : '' }}></td><td class="text-center"><input class="form-check-input" type="radio" name="pelatihan_pascapanen" value="Tidak" {{ old('pelatihan_pascapanen', $pengurus->pelatihan_pascapanen) == 'Tidak' ? 'checked' : '' }}></td></tr>
<tr><td>Pelatihan pengolahan produk (setengah jadi/jadi)</td><td class="text-center"><input class="form-check-input" type="radio" name="pelatihan_pengolahan" value="Ya" {{ old('pelatihan_pengolahan', $pengurus->pelatihan_pengolahan) == 'Ya' ? 'checked' : '' }}></td><td class="text-center"><input class="form-check-input" type="radio" name="pelatihan_pengolahan" value="Tidak" {{ old('pelatihan_pengolahan', $pengurus->pelatihan_pengolahan) == 'Tidak' ? 'checked' : '' }}></td></tr>
<tr><td>Pelatihan manajemen keuangan/pembukuan</td><td class="text-center"><input class="form-check-input" type="radio" name="pelatihan_keuangan" value="Ya" {{ old('pelatihan_keuangan', $pengurus->pelatihan_keuangan) == 'Ya' ? 'checked' : '' }}></td><td class="text-center"><input class="form-check-input" type="radio" name="pelatihan_keuangan" value="Tidak" {{ old('pelatihan_keuangan', $pengurus->pelatihan_keuangan) == 'Tidak' ? 'checked' : '' }}></td></tr>
<tr><td>Pelatihan pengemasan dan pemasaran</td><td class="text-center"><input class="form-check-input" type="radio" name="pelatihan_pemasaran" value="Ya" {{ old('pelatihan_pemasaran', $pengurus->pelatihan_pemasaran) == 'Ya' ? 'checked' : '' }}></td><td class="text-center"><input class="form-check-input" type="radio" name="pelatihan_pemasaran" value="Tidak" {{ old('pelatihan_pemasaran', $pengurus->pelatihan_pemasaran) == 'Tidak' ? 'checked' : '' }}></td></tr>
</tbody>
</table>
</div>
<div class="d-flex align-items-center gap-2 mt-2 mb-5">
<label class="form-label mb-0">Lainnya, sebutkan:</label>
<input type="text" class="form-control form-control-sm w-50" name="pelatihan_lainnya" placeholder="Pelatihan lainnya..." value="{{ old('pelatihan_lainnya', $pengurus->pelatihan_lainnya) }}">
</div>
</div>
<h5 class="pb-3 border-bottom mb-10 mt-4">Kapasitas Pengelolaan</h5>
<div class="row mb-4">
<div class="col-md-6 mb-1">
<h6 class="form-label">Apakah sudah ada pencatatan/pembukuan keuangan?</h6>
<div class="form-check"><input class="form-check-input" type="radio" name="status_pembukuan" value="Sudah" {{ old('status_pembukuan', $pengurus->status_pembukuan) == 'Sudah' ? 'checked' : '' }}><label class="form-check-label">Sudah</label></div>
<div class="form-check"><input class="form-check-input" type="radio" name="status_pembukuan" value="Belum ada" {{ old('status_pembukuan', $pengurus->status_pembukuan) == 'Belum ada' ? 'checked' : '' }}><label class="form-check-label">Belum ada</label></div>
</div>
<div class="col-md-6 mb-3">
<h6 class="form-label">Format pembukuan yang dilakukan:</h6>
<div class="form-check"><input class="form-check-input" type="radio" name="format_pembukuan" value="Manual/buku kas" {{ old('format_pembukuan', $pengurus->format_pembukuan) == 'Manual/buku kas' ? 'checked' : '' }}><label class="form-check-label">Manual/buku kas</label></div>
<div class="form-check"><input class="form-check-input" type="radio" name="format_pembukuan" value="Digital/file Excel" {{ old('format_pembukuan', $pengurus->format_pembukuan) == 'Digital/file Excel' ? 'checked' : '' }}><label class="form-check-label">Digital/file Excel</label></div>
<div class="form-check"><input class="form-check-input" type="radio" name="format_pembukuan" value="Aplikasi khusus" {{ old('format_pembukuan', $pengurus->format_pembukuan) == 'Aplikasi khusus' ? 'checked' : '' }}><label class="form-check-label">Aplikasi keuangan khusus</label></div>
<div class="form-check"><input class="form-check-input" type="radio" name="format_pembukuan" value="Tidak ada" {{ old('format_pembukuan', $pengurus->format_pembukuan) == 'Tidak ada' ? 'checked' : '' }}><label class="form-check-label">Tidak ada</label></div>
</div>
</div>
<div class="mb-4">
<h6 class="form-label mb-3">Berapa kali diadakan pertemuan rutin anggota koperasi untuk membahas <br> rencana kerja dan pertanggungjawaban keuangan?</h6>
<div class="form-check"><input class="form-check-input trigger-toggle" type="radio" name="pertemuan_rutin" value="Satu bulan sekali" data-target="pertemuanLainnya" data-action="hide" {{ old('pertemuan_rutin', $pengurus->pertemuan_rutin) == 'Satu bulan sekali' ? 'checked' : '' }}><label class="form-check-label">Satu bulan sekali</label></div>
<div class="form-check "><input class="form-check-input trigger-toggle" type="radio" name="pertemuan_rutin" value="Satu tahun sekali" data-target="pertemuanLainnya" data-action="hide" {{ old('pertemuan_rutin', $pengurus->pertemuan_rutin) == 'Satu tahun sekali' ? 'checked' : '' }}><label class="form-check-label">Satu tahun sekali</label></div>
<div class="form-check form-check-inline d-inline-flex align-items-center gap-2">
<input class="form-check-input trigger-toggle" type="radio" name="pertemuan_rutin" value="Lainnya" data-target="pertemuanLainnya" data-action="show" {{ old('pertemuan_rutin', $pengurus->pertemuan_rutin) == 'Lainnya' ? 'checked' : '' }}>
<label class="form-check-label text-nowrap">Lainnya:</label>
<input type="text" class="form-control form-control-sm {{ old('pertemuan_rutin', $pengurus->pertemuan_rutin) == 'Lainnya' ? '' : 'd-none' }}" id="pertemuanLainnya" name="pertemuan_lainnya" placeholder="..." value="{{ old('pertemuan_lainnya', $pengurus->pertemuan_lainnya) }}">
</div>
</div>
<div id="blok-simpan-pinjam" class="p-3 mb-4 rounded border border-primary bg-label-primary {{ str_contains($pengurus->layanan_koperasi ?? '', 'Simpan pinjam') ? '' : 'd-none' }}">
<h6 class="fw-bold text-primary mb-3"><i class="ti tabler-info-circle me-1"></i>Khusus Koperasi Simpan Pinjam</h6>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">Tingkat/persentase kredit macet di koperasi?</label>
<select class="form-select form-select-sm" name="kasus_kredit_macet">
<option value="" selected disabled>Pilih...</option>
<option value="<10%" {{ old('kasus_kredit_macet', $pengurus->kasus_kredit_macet) == '<10%' ? 'selected' : '' }}> < 10% </option>
<option value="10-25%" {{ old('kasus_kredit_macet', $pengurus->kasus_kredit_macet) == '10-25%' ? 'selected' : '' }}>10 - 25%</option>
<option value="25-50%" {{ old('kasus_kredit_macet', $pengurus->kasus_kredit_macet) == '25-50%' ? 'selected' : '' }}>25 - 50%</option>
<option value="50-75%" {{ old('kasus_kredit_macet', $pengurus->kasus_kredit_macet) == '50-75%' ? 'selected' : '' }}>50 - 75%</option>
<option value=">75%" {{ old('kasus_kredit_macet', $pengurus->kasus_kredit_macet) == '>75%' ? 'selected' : '' }}> > 75% </option>
</select>
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Apakah ada sanksi untuk anggota dengan kredit macet?</label>
<div class="form-check form-check-inline"><input class="form-check-input" type="radio" name="sanksi_kredit_macet" value="Ada" {{ old('sanksi_kredit_macet', $pengurus->sanksi_kredit_macet) == 'Ada' ? 'checked' : '' }}><label class="form-check-label">Ada</label></div>
<div class="form-check form-check-inline"><input class="form-check-input" type="radio" name="sanksi_kredit_macet" value="Tidak ada" {{ old('sanksi_kredit_macet', $pengurus->sanksi_kredit_macet) == 'Tidak ada' ? 'checked' : '' }}><label class="form-check-label">Tidak ada</label></div>
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Jika ada, apakah sanksi tersebut efektif?</label>
<select class="form-select form-select-sm" name="efektivitas_sanksi">
<option value="" selected disabled>Pilih efektivitas...</option>
<option value="Sangat efektif" {{ old('efektivitas_sanksi', $pengurus->efektivitas_sanksi) == 'Sangat efektif' ? 'selected' : '' }}>Sangat efektif</option>
<option value="Efektif" {{ old('efektivitas_sanksi', $pengurus->efektivitas_sanksi) == 'Efektif' ? 'selected' : '' }}>Efektif</option>
<option value="Biasa Saja" {{ old('efektivitas_sanksi', $pengurus->efektivitas_sanksi) == 'Biasa Saja' ? 'selected' : '' }}>Biasa Saja</option>
<option value="Kurang Efektif" {{ old('efektivitas_sanksi', $pengurus->efektivitas_sanksi) == 'Kurang Efektif' ? 'selected' : '' }}>Kurang Efektif</option>
<option value="Tidak Efektif" {{ old('efektivitas_sanksi', $pengurus->efektivitas_sanksi) == 'Tidak Efektif' ? 'selected' : '' }}>Tidak Efektif</option>
</select>
</div>
</div>
</div>
<div class="mb-10">
<h6 class="form-label mb-3">Apa faktor utama yang menyebabkan manajemen pengelolaan dalam koperasi efektif/tidak efektif?</h6>
<div class="form-check "><input class="form-check-input" type="checkbox" name="faktor_manajemen[]" value="Rasa saling percaya" {{ str_contains($pengurus->faktor_manajemen ?? '', 'Rasa saling percaya') ? 'checked' : '' }}><label class="form-check-label">Rasa saling percaya</label></div>
<div class="form-check "><input class="form-check-input" type="checkbox" name="faktor_manajemen[]" value="Kepemimpinan koperasi" {{ str_contains($pengurus->faktor_manajemen ?? '', 'Kepemimpinan koperasi') ? 'checked' : '' }}><label class="form-check-label">Kepemimpinan koperasi</label></div>
<div class="form-check "><input class="form-check-input" type="checkbox" name="faktor_manajemen[]" value="Jumlah anggota" {{ str_contains($pengurus->faktor_manajemen ?? '', 'Jumlah anggota') ? 'checked' : '' }}><label class="form-check-label">Jumlah anggota</label></div>
<div class="form-check "><input class="form-check-input" type="checkbox" name="faktor_manajemen[]" value="Catatan/pembukuan rapi" {{ str_contains($pengurus->faktor_manajemen ?? '', 'Catatan/pembukuan rapi') ? 'checked' : '' }}><label class="form-check-label">Catatan/pembukuan rapi</label></div>
<div class="form-check d-inline-flex align-items-center gap-2 mt-2 mt-md-0">
<input class="form-check-input trigger-toggle-check" type="checkbox" name="faktor_manajemen[]" value="Lainnya" data-target="faktorLainnya" {{ str_contains($pengurus->faktor_manajemen ?? '', 'Lainnya') ? 'checked' : '' }}>
<label class="form-check-label text-nowrap">Lainnya, Sebutkan:</label>
<input type="text" class="form-control form-control-sm {{ str_contains($pengurus->faktor_manajemen ?? '', 'Lainnya') ? '' : 'd-none' }}" id="faktorLainnya" name="faktor_manajemen_lainnya" placeholder="..." value="{{ old('faktor_manajemen_lainnya', $pengurus->faktor_manajemen_lainnya) }}">
</div>
</div>
<h5 class="pb-3 border-bottom mb-10 mt-4">Pengalaman dalam mengelola rantai nilai komoditas pertanian</h5>
<div class="mb-4">
<label class="form-label">Apakah koperasi Bapak/Ibu terlibat dalam proses rantai nilai berikut ini?</label>
<div class="row g-2">
<div class="col-md-6"><div class="form-check"><input class="form-check-input" type="checkbox" name="keterlibatan_rantai_nilai[]" value="Penyediaan input" {{ str_contains($pengurus->keterlibatan_rantai_nilai ?? '', 'Penyediaan input') ? 'checked' : '' }}><label class="form-check-label">Penyediaan input (pupuk, bibit, dll)</label></div></div>
<div class="col-md-6"><div class="form-check"><input class="form-check-input" type="checkbox" name="keterlibatan_rantai_nilai[]" value="Agregasi" {{ str_contains($pengurus->keterlibatan_rantai_nilai ?? '', 'Agregasi') ? 'checked' : '' }}><label class="form-check-label">Agregasi (pengumpulan) produk pertanian dan perkebunan</label></div></div>
<div class="col-md-6"><div class="form-check"><input class="form-check-input" type="checkbox" name="keterlibatan_rantai_nilai[]" value="Pemasaran mentah" {{ str_contains($pengurus->keterlibatan_rantai_nilai ?? '', 'Pemasaran mentah') ? 'checked' : '' }}><label class="form-check-label">Pemasaran produk mentah (belum diolah)</label></div></div>
<div class="col-md-6"><div class="form-check"><input class="form-check-input" type="checkbox" name="keterlibatan_rantai_nilai[]" value="Pengolahan barang jadi" {{ str_contains($pengurus->keterlibatan_rantai_nilai ?? '', 'Pengolahan barang jadi') ? 'checked' : '' }}><label class="form-check-label">Pengolahan produk pangan menjadi bahan setengah jadi/barang jadi</label></div></div>
<div class="col-md-6"><div class="form-check"><input class="form-check-input" type="checkbox" name="keterlibatan_rantai_nilai[]" value="Pemasaran olahan" {{ str_contains($pengurus->keterlibatan_rantai_nilai ?? '', 'Pemasaran olahan') ? 'checked' : '' }}><label class="form-check-label">Pemasaran produk pertanian olahan</label></div></div>
<div class="col-md-6"><div class="form-check"><input class="form-check-input" type="checkbox" name="keterlibatan_rantai_nilai[]" value="Jual langsung konsumen" {{ str_contains($pengurus->keterlibatan_rantai_nilai ?? '', 'Jual langsung konsumen') ? 'checked' : '' }}><label class="form-check-label">Penjualan langsung ke konsumen akhir</label></div></div>
<div class="col-md-6"><div class="form-check"><input class="form-check-input" type="checkbox" name="keterlibatan_rantai_nilai[]" value="Tidak terlibat kegiatan di atas" {{ str_contains($pengurus->keterlibatan_rantai_nilai ?? '', 'Tidak terlibat kegiatan di atas') ? 'checked' : '' }}><label class="form-check-label">Tidak terlibat kegiatan di atas</label></div></div>
</div>
</div>
<div class="mb-10">
<h6 class="mb-3">Jika terlibat dalam proses agregasi, komoditas apa saja yang di agregasi oleh koperasi?</h6>
<div class="form-check form-check-inline mt-2"><input class="form-check-input opsi-agregasi" type="checkbox" name="agregasi_komoditas[]" value="Kopi" {{ str_contains($pengurus->agregasi_komoditas ?? '', 'Kopi') ? 'checked' : '' }}><label class="form-check-label">Kopi</label></div>
<div class="form-check form-check-inline mt-2"><input class="form-check-input opsi-agregasi" type="checkbox" name="agregasi_komoditas[]" value="Kakao" {{ str_contains($pengurus->agregasi_komoditas ?? '', 'Kakao') ? 'checked' : '' }}><label class="form-check-label">Kakao</label></div>
<div class="form-check form-check-inline mt-2"><input class="form-check-input opsi-agregasi" type="checkbox" name="agregasi_komoditas[]" value="Vanili" {{ str_contains($pengurus->agregasi_komoditas ?? '', 'Vanili') ? 'checked' : '' }}><label class="form-check-label">Vanili</label></div>
<div class="form-check form-check-inline mt-2"><input class="form-check-input opsi-agregasi" type="checkbox" name="agregasi_komoditas[]" value="Kemiri" {{ str_contains($pengurus->agregasi_komoditas ?? '', 'Kemiri') ? 'checked' : '' }}><label class="form-check-label">Kemiri</label></div>
<div class="form-check form-check-inline d-inline-flex align-items-center gap-2 mt-2">
<input class="form-check-input trigger-toggle-check opsi-agregasi" type="checkbox" name="agregasi_komoditas[]" value="Lainnya" data-target="komoditasLainnya" {{ str_contains($pengurus->agregasi_komoditas ?? '', 'Lainnya') ? 'checked' : '' }}>
<label class="form-check-label text-nowrap">Lainnya, sebutkan:</label>
<input type="text" class="form-control form-control-sm w-auto {{ str_contains($pengurus->agregasi_komoditas ?? '', 'Lainnya') ? '' : 'd-none' }}" id="komoditasLainnya" name="agregasi_komoditas_lainnya" placeholder="..." value="{{ old('agregasi_komoditas_lainnya', $pengurus->agregasi_komoditas_lainnya) }}">
</div>
<div class="form-check mt-3">
<input class="form-check-input" type="checkbox" name="agregasi_komoditas[]" value="Tidak terlibat" id="triggerSkipD6" {{ str_contains($pengurus->agregasi_komoditas ?? '', 'Tidak terlibat') ? 'checked' : '' }}>
<label class="form-check-label" for="triggerSkipD6">Tidak terlibat proses agregasi <small class="text-danger">(Mencentang ini akan melewati bagian D6)</small></label>
</div>
</div>
<div id="blok-sisa-d5" style="{{ str_contains($pengurus->agregasi_komoditas ?? '', 'Tidak terlibat') ? 'display: none;' : 'display: block;' }}">
<div class="mb-10">
<h6 class="mb-3">Fasilitas pengolahan pascapanen apa saja yang dikelola oleh koperasi saat ini?</h6>
<div class="form-check form-check-inline"><input class="form-check-input" type="checkbox" name="fasilitas_koperasi[]" value="Gudang penyimpanan" {{ str_contains($pengurus->fasilitas_koperasi ?? '', 'Gudang penyimpanan') ? 'checked' : '' }}><label class="form-check-label">Gudang</label></div>
<div class="form-check form-check-inline"><input class="form-check-input" type="checkbox" name="fasilitas_koperasi[]" value="Alat pengering" {{ str_contains($pengurus->fasilitas_koperasi ?? '', 'Alat pengering') ? 'checked' : '' }}><label class="form-check-label">Alat pengering</label></div>
<div class="form-check form-check-inline"><input class="form-check-input" type="checkbox" name="fasilitas_koperasi[]" value="Mesin pemecah" {{ str_contains($pengurus->fasilitas_koperasi ?? '', 'Mesin pemecah') ? 'checked' : '' }}><label class="form-check-label">Mesin pemecah</label></div>
<div class="form-check form-check-inline"><input class="form-check-input" type="checkbox" name="fasilitas_koperasi[]" value="Mesin penggiling" {{ str_contains($pengurus->fasilitas_koperasi ?? '', 'Mesin penggiling') ? 'checked' : '' }}><label class="form-check-label">Mesin penggiling</label></div>
<div class="form-check form-check-inline"><input class="form-check-input" type="checkbox" name="fasilitas_koperasi[]" value="Mesin sortasi" {{ str_contains($pengurus->fasilitas_koperasi ?? '', 'Mesin sortasi') ? 'checked' : '' }}><label class="form-check-label">Mesin sortasi</label></div>
<div class="form-check form-check-inline d-inline-flex align-items-center gap-2">
<input class="form-check-input trigger-toggle-check" type="checkbox" name="fasilitas_koperasi[]" value="Lainnya" data-target="fasilitasLainnya" {{ str_contains($pengurus->fasilitas_koperasi ?? '', 'Lainnya') ? 'checked' : '' }}>
<label class="form-check-label text-nowrap">Lainnya:</label>
<input type="text" class="form-control form-control-sm {{ str_contains($pengurus->fasilitas_koperasi ?? '', 'Lainnya') ? '' : 'd-none' }}" id="fasilitasLainnya" name="fasilitas_lainnya" placeholder="..." value="{{ old('fasilitas_lainnya', $pengurus->fasilitas_lainnya) }}">
</div>
</div>
<div class="mb-10">
<h6 class="mb-3">Apakah koperasi melakukan pengolahan lanjutan untuk menaikkan nilai jual <br> (seperti pengeringan, fermentasi, penepungan, pengemasan)?</h6>
<div class="form-check d-flex align-items-center gap-2 mb-2">
<input class="form-check-input trigger-toggle" type="radio" name="pengolahan_lanjutan" value="Ya" id="pengolahanYa" data-target="pengolahanYaInput" data-action="show" {{ old('pengolahan_lanjutan', $pengurus->pengolahan_lanjutan) == 'Ya' ? 'checked' : '' }}>
<label class="form-check-label text-nowrap" for="pengolahanYa">Ya, sebutkan:</label>
<input type="text" class="form-control form-control-sm w-50 {{ old('pengolahan_lanjutan', $pengurus->pengolahan_lanjutan) == 'Ya' ? '' : 'd-none' }}" id="pengolahanYaInput" name="detail_pengolahan_lanjutan" placeholder="Contoh: Fermentasi biji kopi" value="{{ old('detail_pengolahan_lanjutan', $pengurus->detail_pengolahan_lanjutan) }}">
</div>
<div class="form-check">
<input class="form-check-input trigger-toggle" type="radio" name="pengolahan_lanjutan" value="Tidak" id="pengolahanTidak" data-target="pengolahanYaInput" data-action="hide" {{ old('pengolahan_lanjutan', $pengurus->pengolahan_lanjutan) == 'Tidak' ? 'checked' : '' }}>
<label class="form-check-label" for="pengolahanTidak">Tidak</label>
</div>
</div>
<div class="mb-10">
<h6 class="mb-3">Bagaimana cara pengurus koperasi menentukan harga beli di tingkat petani anggota? (Transparan/fluktuasi/kualitas mutu)</h6>
<textarea class="form-control" name="cara_penentuan_harga" rows="3" placeholder="Jelaskan mekanisme penetapan harga...">{{ old('cara_penentuan_harga', $pengurus->cara_penentuan_harga) }}</textarea>
</div>
<div class="mb-4">
<h6 class="mb-3">Adakah kontrak kerjasama antara koperasi dengan mitra pembeli utama (offtaker) atau perusahaan?</h6>
<div class="form-check d-flex align-items-center gap-2 mb-2">
<input class="form-check-input trigger-toggle" type="radio" name="kontrak_offtaker" value="Ada" id="kontrakAda" data-target="kontrakAdaInput" data-action="show" {{ old('kontrak_offtaker', $pengurus->kontrak_offtaker) == 'Ada' ? 'checked' : '' }}>
<label class="form-check-label text-nowrap" for="kontrakAda">Ada, sebutkan komoditasnya:</label>
<input type="text" class="form-control form-control-sm w-50 {{ old('kontrak_offtaker', $pengurus->kontrak_offtaker) == 'Ada' ? '' : 'd-none' }}" id="kontrakAdaInput" name="kontrak_komoditas" placeholder="Contoh: Kopi Robusta" value="{{ old('kontrak_komoditas', $pengurus->kontrak_komoditas) }}">
</div>
<div class="form-check">
<input class="form-check-input trigger-toggle" type="radio" name="kontrak_offtaker" value="Tidak ada" data-target="kontrakAdaInput" data-action="hide" {{ old('kontrak_offtaker', $pengurus->kontrak_offtaker) == 'Tidak ada' ? 'checked' : '' }}>
<label class="form-check-label" for="kontrakAda">Tidak ada</label>
</div>
</div>
</div>
<div class="d-flex justify-content-between mt-5 border-top pt-4">
<div></div>
<div class="ms-auto">
<button type="submit" class="btn btn-primary">
Simpan & Selanjutnya <i class="ti tabler-arrow-right ms-1"></i>
</button>
</div>
</div>
</form>
</div>
</div>
</div>
@endsection
@push('scripts')
<script>
$(document).ready(function() {
$('.trigger-toggle').change(function() {
let target = $(this).data('target');
let action = $(this).data('action');
if (action === 'show') {
$('#' + target).removeClass('d-none').focus();
} else {
$('#' + target).addClass('d-none').val('');
}
});
$('.trigger-toggle-check').change(function() {
let target = $(this).data('target');
if ($(this).is(':checked')) {
$('#' + target).removeClass('d-none').focus();
} else {
$('#' + target).addClass('d-none').val('');
}
});
$('#cekSimpanPinjam').on('change', function() {
if($(this).is(':checked')) {
$('#blok-simpan-pinjam').removeClass('d-none').hide().slideDown();
} else {
$('#blok-simpan-pinjam').slideUp(function() {
$(this).addClass('d-none');
});
}
});
$('#triggerSkipD6').on('change', function() {
if ($(this).is(':checked')) {
$('#blok-sisa-d5').slideUp();
$('.opsi-agregasi').prop('checked', false);
$('#komoditasLainnya').addClass('d-none').val('');
} else {
$('#blok-sisa-d5').slideDown();
}
});
$('.opsi-agregasi').on('change', function() {
if ($(this).is(':checked')) {
$('#triggerSkipD6').prop('checked', false);
$('#blok-sisa-d5').slideDown();
}
});
});
</script>
@endpush
@@ -0,0 +1,149 @@
<h6 class="fw-bold mb-3">Data Pribadi Responden</h6>
<div class="row mb-4">
<div class="col-md-6 mb-3">
<label class="form-label">Nama Lengkap <span class="text-danger">*</span></label>
<input type="text" class="form-control @error('nama_lengkap') is-invalid @enderror" name="nama_lengkap" value="{{ old('nama_lengkap', $pengurus->nama_lengkap ?? '') }}" placeholder="Contoh: Budi Santoso" required>
@error('nama_lengkap')<div class="invalid-feedback">{{ $message }}</div>@enderror
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Posisi dalam Koperasi <span class="text-danger">*</span></label>
<select class="form-select @error('posisi_dalam_koperasi') is-invalid @enderror" name="posisi_dalam_koperasi" required>
<option value="" disabled {{ !isset($pengurus) ? 'selected' : '' }}>Pilih Posisi</option>
<option value="Pengurus koperasi" {{ old('posisi_dalam_koperasi', $pengurus->posisi_dalam_koperasi ?? '') == 'Pengurus koperasi' ? 'selected' : '' }}>Pengurus koperasi</option>
<option value="Anggota koperasi" {{ old('posisi_dalam_koperasi', $pengurus->posisi_dalam_koperasi ?? '') == 'Anggota koperasi' ? 'selected' : '' }}>Anggota koperasi</option>
<option value="Pendamping koperasi" {{ old('posisi_dalam_koperasi', $pengurus->posisi_dalam_koperasi ?? '') == 'Pendamping koperasi' ? 'selected' : '' }}>Pendamping koperasi</option>
</select>
@error('posisi_dalam_koperasi')<div class="invalid-feedback">{{ $message }}</div>@enderror
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Tahun Bergabung Koperasi</label>
<input type="number" class="form-control @error('tahun_bergabung') is-invalid @enderror" name="tahun_bergabung" value="{{ old('tahun_bergabung', $pengurus->tahun_bergabung ?? '') }}" placeholder="Contoh: 2020" min="1900" max="{{ date('Y') }}">
</div>
<div class="col-md-6 mb-3">
<label class="form-label">No. Handphone</label>
<input type="text" class="form-control @error('no_hp') is-invalid @enderror" name="no_hp" value="{{ old('no_hp', $pengurus->no_hp ?? '') }}" placeholder="Contoh: 08123456789">
</div>
<div class="col-md-12 mb-3">
<label class="form-label">Alamat Tinggal</label>
<textarea class="form-control @error('alamat_tinggal') is-invalid @enderror" name="alamat_tinggal" rows="2" placeholder="Masukkan alamat domisili lengkap">{{ old('alamat_tinggal', $pengurus->alamat_tinggal ?? '') }}</textarea>
</div>
</div>
<h6 class="fw-bold mb-3 pt-3 border-top">Profil Koperasi</h6>
<div class="mb-3">
<label class="form-label">Nama Koperasi <span class="text-danger">*</span></label>
<input type="text" class="form-control @error('nama_koperasi') is-invalid @enderror" name="nama_koperasi" value="{{ old('nama_koperasi', $pengurus->nama_koperasi ?? '') }}" placeholder="Contoh: Koperasi Tani Makmur" required>
@error('nama_koperasi')<div class="invalid-feedback">{{ $message }}</div>@enderror
</div>
<div class="mb-3">
<label class="form-label d-block">Jenis Koperasi <span class="text-danger">*</span></label>
<div class="row g-2">
@foreach(['Koperasi Produksi', 'Koperasi Simpan Pinjam', 'Koperasi Pemasaran', 'Koperasi Desa'] as $jenis)
<div class="col-md-3">
<div class="form-check">
<input class="form-check-input" type="radio" name="jenis_koperasi" id="jenis_{{ $loop->index }}" value="{{ $jenis }}" {{ old('jenis_koperasi', $pengurus->jenis_koperasi ?? '') == $jenis ? 'checked' : '' }} required>
<label class="form-check-label" for="jenis_{{ $loop->index }}">{{ $jenis }}</label>
</div>
</div>
@endforeach
</div>
<div class="mt-2 d-flex align-items-center">
@php
$isLainnya = isset($pengurus) && !in_array($pengurus->jenis_koperasi, ['Koperasi Produksi', 'Koperasi Simpan Pinjam', 'Koperasi Pemasaran', 'Koperasi Desa']) && $pengurus->jenis_koperasi != null;
@endphp
<div class="form-check me-3">
<input class="form-check-input" type="radio" name="jenis_koperasi" id="jenis_lainnya" value="Lainnya" {{ old('jenis_koperasi') == 'Lainnya' || $isLainnya ? 'checked' : '' }}>
<label class="form-check-label" for="jenis_lainnya">Lainnya, sebutkan:</label>
</div>
<input type="text" class="form-control form-control-sm w-50" name="jenis_koperasi_lainnya" value="{{ old('jenis_koperasi_lainnya', $isLainnya ? $pengurus->jenis_koperasi : '') }}" placeholder="Sebutkan jenis koperasi...">
</div>
</div>
<div class="mb-3">
<label class="form-label">Alamat Koperasi</label>
<textarea class="form-control @error('alamat_koperasi') is-invalid @enderror" name="alamat_koperasi" rows="2" placeholder="Masukkan alamat lengkap koperasi">{{ old('alamat_koperasi', $pengurus->alamat_koperasi ?? '') }}</textarea>
</div>
<div class="mb-3">
<label class="form-label d-block">Skala Operasi Koperasi <span class="text-danger">*</span></label>
<div class="row g-2">
@foreach(['Level Dusun', 'Lintas Dusun', 'Level Desa', 'Lintas Desa', 'Level Kecamatan', 'Lintas Kecamatan', 'Level Kabupaten', 'Lintas Kabupaten'] as $skala)
<div class="col-md-3">
<div class="form-check">
<input class="form-check-input" type="radio" name="skala_operasi" id="skala_{{ $loop->index }}" value="{{ $skala }}" {{ old('skala_operasi', $pengurus->skala_operasi ?? '') == $skala ? 'checked' : '' }} required>
<label class="form-check-label" for="skala_{{ $loop->index }}">{{ $skala }}</label>
</div>
</div>
@endforeach
</div>
</div>
<div class="row mb-3">
<div class="col-md-6 mb-3">
<label class="form-label">Tahun Berdiri</label>
<input type="number" class="form-control" name="tahun_berdiri" value="{{ old('tahun_berdiri', $pengurus->tahun_berdiri ?? '') }}" placeholder="Contoh: 2015" min="1900" max="{{ date('Y') }}">
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Jumlah Anggota Awal Berdiri</label>
<div class="input-group">
<input type="number" class="form-control" name="jumlah_anggota_awal" value="{{ old('jumlah_anggota_awal', $pengurus->jumlah_anggota_awal ?? '') }}" placeholder="0" min="0">
<span class="input-group-text">orang</span>
</div>
</div>
</div>
<div class="row mb-3">
<div class="col-md-6 mb-3">
<label class="form-label">Jumlah Anggota (per Desember 2025)</label>
<div class="row g-2">
<div class="col-6">
<div class="input-group">
<input type="number" class="form-control" name="jumlah_anggota_dewasa" value="{{ old('jumlah_anggota_dewasa', $pengurus->jumlah_anggota_dewasa ?? '') }}" placeholder="Dewasa" min="0">
<span class="input-group-text">dewasa</span>
</div>
</div>
<div class="col-6">
<div class="input-group">
<input type="number" class="form-control" name="jumlah_anggota_anak" value="{{ old('jumlah_anggota_anak', $pengurus->jumlah_anggota_anak ?? '') }}" placeholder="Anak-anak" min="0">
<span class="input-group-text">anak-anak</span>
</div>
</div>
</div>
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Jumlah Pengurus Sekarang</label>
<div class="input-group">
<input type="number" class="form-control" name="jumlah_pengurus" value="{{ old('jumlah_pengurus', $pengurus->jumlah_pengurus ?? '') }}" placeholder="0" min="0">
<span class="input-group-text">orang</span>
</div>
</div>
</div>
<div class="mb-4">
<label class="form-label d-block fw-semibold">Jumlah Sisa Hasil Usaha (SHU)</label>
<div class="row g-3">
<div class="col-md-4">
<label class="form-label small text-muted">2025:</label>
<div class="input-group">
<span class="input-group-text">Rp.</span>
<input type="number" class="form-control" name="shu_2025" value="{{ old('shu_2025', $pengurus->shu_2025 ?? '') }}" placeholder="0">
</div>
</div>
<div class="col-md-4">
<label class="form-label small text-muted">2024:</label>
<div class="input-group">
<span class="input-group-text">Rp.</span>
<input type="number" class="form-control" name="shu_2024" value="{{ old('shu_2024', $pengurus->shu_2024 ?? '') }}" placeholder="0">
</div>
</div>
<div class="col-md-4">
<label class="form-label small text-muted">2023:</label>
<div class="input-group">
<span class="input-group-text">Rp.</span>
<input type="number" class="form-control" name="shu_2023" value="{{ old('shu_2023', $pengurus->shu_2023 ?? '') }}" placeholder="0">
</div>
</div>
</div>
</div>
@@ -0,0 +1,35 @@
@extends('layout.main')
@section('content')
<div class="container-xxl flex-grow-1 container-p-y">
@if ($errors->any())
<div class="alert alert-danger">
<ul class="mb-0">
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
@include('koperasi.profil.header', ['pengurus' => null])
<div class="card mb-6">
<div class="card-header border-bottom">
<h5 class="card-title mb-0">Tambah Profil Dasar Koperasi</h5>
</div>
<div class="card-body pt-6">
<form action="{{ route('koperasi.pengurus.store') }}" method="POST">
@csrf
@include('koperasi.profil._form', ['pengurus' => null])
<div class="mt-4 text-end">
<a href="{{ route('koperasi.pengurus.index') }}" class="btn btn-secondary me-2 wave-effect wave-light">Batal / Kembali</a>
<button type="submit" class="btn btn-success data-submit wave-effect wave-light"><i class="ti tabler-device-floppy me-2"></i> Simpan Profil</button>
</div>
</form>
</div>
</div>
</div>
@endsection
@@ -0,0 +1,38 @@
@extends('layout.main')
@section('content')
<div class="container-xxl flex-grow-1 container-p-y">
@if ($errors->any())
<div class="alert alert-danger">
<ul class="mb-0">
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
@include('koperasi.profil.header', ['pengurus' => $pengurus])
<div class="card mb-6">
<div class="card-header border-bottom">
<h5 class="card-title mb-0">Edit Profil Dasar Koperasi</h5>
</div>
<div class="card-body pt-6">
<form action="{{ route('koperasi.pengurus.update', $pengurus->id) }}" method="POST">
@csrf
@method('PUT')
@include('koperasi.profil._form', ['pengurus' => $pengurus])
<div class="mt-4 text-end">
<a href="{{ route('koperasi.pengurus.index') }}" class="btn btn-secondary me-2 wave-effect wave-light">Batal / Kembali</a>
<button type="submit" class="btn btn-success data-submit wave-effect wave-light">
<i class="ti tabler-device-floppy me-2"></i> Simpan Perubahan
</button>
</div>
</form>
</div>
</div>
</div>
@endsection
@@ -0,0 +1,191 @@
<style>
@media (min-width: 768px) {
.nav-pills-custom-spacing {
display: flex;
width: 100%;
gap: 0.5rem;
flex-wrap: nowrap !important;
overflow-x: auto;
}
.nav-pills-custom-spacing::-webkit-scrollbar {
display: none;
}
.nav-pills-custom-spacing .nav-item {
margin-right: 0 !important;
flex: 1;
}
.nav-pills-custom-spacing .nav-link {
display: flex;
justify-content: center;
align-items: center;
padding-left: 0.5rem !important;
padding-right: 0.5rem !important;
width: 100%;
white-space: nowrap;
font-size: 0.9375rem !important;
}
.nav-pills-custom-spacing .nav-link i {
margin-right: 0.4rem !important;
}
}
</style>
@php
$namaLengkap = isset($pengurus) ? $pengurus->nama_lengkap : 'Tambah Profil Baru';
$namaKoperasi = isset($pengurus) ? $pengurus->nama_koperasi : 'Nama Koperasi';
$jabatanLabel = isset($pengurus) ? $pengurus->posisi_dalam_koperasi : 'Posisi/Jabatan';
$noHp = isset($pengurus) ? $pengurus->no_hp : 'Belum terisi';
$domisili = isset($pengurus) ? $pengurus->alamat_tinggal : 'Alamat Domisili Pengurus';
@endphp
<div class="row mb-4">
<div class="col-md-4 mb-2 mb-md-0">
<div class="card h-100">
<div class="card-body p-3">
<h6 class="card-title mb-2">Profil Pengurus Koperasi</h6>
<div class="d-flex justify-content-start align-items-center mb-3">
<div class="avatar me-2">
<span class="avatar-initial rounded bg-label-primary">
<i class="icon-base ti tabler-user-check icon-22px"></i>
</span>
</div>
<div class="d-flex flex-column">
<h6 class="mb-0" style="font-size: 0.9rem;">{{ $namaLengkap }}</h6>
<span class="text-muted" style="font-size: 0.75rem;">{{ $namaKoperasi }}</span>
<span class="text-muted" style="font-size: 0.75rem;">Jabatan: {{ $jabatanLabel }}</span>
</div>
</div>
<hr class="my-2">
<h6 class="mb-1" style="font-size: 0.85rem;">Kontak & Domisili</h6>
<p class="mb-0 text-muted" style="font-size: 0.75rem;">No. HP: {{ $noHp }}</p>
<p class="mb-0 text-muted" style="font-size: 0.75rem; line-height: 1.3;">{{ $domisili }}</p>
</div>
</div>
</div>
<div class="col-md-8 d-flex flex-column">
<div class="card mb-2 flex-grow-1">
<div class="card-widget-separator-wrapper h-100">
<div class="card-body card-widget-separator py-2 h-100 d-flex flex-column justify-content-center">
<div class="row gy-2">
<div class="col-sm-6 col-lg-4">
<div class="d-flex justify-content-between align-items-center card-widget-1 border-end h-100 pe-3">
<div>
<p class="mb-0 text-muted" style="font-size: 0.75rem;">Koperasi</p>
<h5 class="mb-0 text-truncate" style="max-width: 120px;" title="{{ $namaKoperasi }}">{{ $namaKoperasi }}</h5>
<p class="mb-0" style="font-size: 0.75rem;">
<span class="me-1">{{ isset($pengurus) ? $pengurus->jenis_koperasi : 'Belum terisi' }}</span>
</p>
</div>
<span class="avatar">
<span class="avatar-initial rounded">
<i class="icon-base ti tabler-building-store icon-22px text-heading"></i>
</span>
</span>
</div>
</div>
<div class="col-sm-6 col-lg-4">
<div class="d-flex justify-content-between align-items-center card-widget-1 border-end h-100 px-3">
<div>
<p class="mb-0 text-muted" style="font-size: 0.75rem;">Posisi</p>
<h5 class="mb-0">{{ $jabatanLabel }}</h5>
<p class="mb-0" style="font-size: 0.75rem;">
<span class="me-1">Thn Gabung: {{ isset($pengurus) ? ($pengurus->tahun_bergabung ?: '-') : '-' }}</span>
</p>
</div>
<span class="avatar">
<span class="avatar-initial rounded">
<i class="icon-base ti tabler-briefcase icon-22px text-heading"></i>
</span>
</span>
</div>
</div>
<div class="col-sm-6 col-lg-4">
<div class="d-flex justify-content-between align-items-center card-widget-1 h-100 ps-3">
<div>
<p class="mb-0 text-muted" style="font-size: 0.75rem;">Skala Operasi</p>
<h5 class="mb-0">{{ isset($pengurus) ? $pengurus->skala_operasi : '-' }}</h5>
</div>
<span class="avatar">
<span class="avatar-initial rounded">
<i class="icon-base ti tabler-map-pin icon-22px text-heading"></i>
</span>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="card flex-grow-1">
<div class="card-body py-3 d-flex align-items-center">
<div class="w-100">
<p class="mb-1 text-muted" style="font-size: 0.75rem;">Alamat Koperasi</p>
<h6 class="mb-0">{{ isset($pengurus) ? ($pengurus->alamat_koperasi ?: '-') : '-' }}</h6>
</div>
</div>
</div>
</div>
</div>
<div class="nav-align-top">
<ul class="nav nav-pills flex-column flex-md-row mb-6 row-gap-2 nav-pills-custom-spacing" id="main-tab-nav">
<li class="nav-item">
<a class="nav-link {{ request()->routeIs('koperasi.pengurus.create') || request()->routeIs('koperasi.pengurus.edit') ? 'active' : '' }}" href="{{ isset($pengurus) ? route('koperasi.pengurus.edit', $pengurus->id) : route('koperasi.pengurus.create') }}">
<i class="icon-base ti tabler-user-check icon-sm"></i>Profil Dasar
</a>
</li>
@php
$currentStep = request()->query('step', 1);
$isSurvey = request()->routeIs('koperasi.pengurus.survey');
$disabledAttr = !isset($pengurus) ? 'disabled data-bs-toggle="tooltip" data-bs-placement="top" title="Simpan profil dasar terlebih dahulu"' : '';
@endphp
<li class="nav-item">
<a class="nav-link {{ ($isSurvey && $currentStep == 1) ? 'active bg-primary text-white' : 'text-muted' }}"
href="{{ isset($pengurus) ? route('koperasi.pengurus.survey', [$pengurus->id, 'step' => 1]) : '#' }}" {!! $disabledAttr !!}>
<i class="icon-base ti tabler-building-bank icon-sm"></i>Kelembagaan-Aktor Sistem
</a>
</li>
<li class="nav-item">
<a class="nav-link {{ ($isSurvey && $currentStep == 2) ? 'active bg-primary text-white' : 'text-muted' }}"
href="{{ isset($pengurus) ? route('koperasi.pengurus.survey', [$pengurus->id, 'step' => 2]) : '#' }}" {!! $disabledAttr !!}>
<i class="icon-base ti tabler-truck-delivery icon-sm"></i>Rantai Nilai-Akses Pasar
</a>
</li>
<li class="nav-item">
<a class="nav-link {{ ($isSurvey && $currentStep == 3) ? 'active bg-primary text-white' : 'text-muted' }}"
href="{{ isset($pengurus) ? route('koperasi.pengurus.survey', [$pengurus->id, 'step' => 3]) : '#' }}" {!! $disabledAttr !!}>
<i class="icon-base ti tabler-users icon-sm"></i>Sosial Ekonomi
</a>
</li>
</ul>
</div>
<script>
function jumpToStep(step) {
$('.step-content').addClass('d-none');
$('#step-' + step).removeClass('d-none');
$('.survey-tab-link').removeClass('active');
$('#tab-step-' + step).addClass('active');
if (history.pushState) {
window.history.replaceState(null, null, '#step-' + step);
}
}
$(document).ready(function() {
let hash = window.location.hash;
if (hash && hash.startsWith('#step-')) {
let stepNum = parseInt(hash.replace('#step-', ''));
if (stepNum >= 1 && stepNum <= 4) {
jumpToStep(stepNum);
}
}
});
</script>
@@ -0,0 +1,160 @@
@extends('layout.main')
@section('content')
<div class="container-xxl flex-grow-1 container-p-y">
<div class="d-flex flex-row gap-3 mb-4">
<div class="w-25">
<x-summary-card
title="Pengurus Koperasi"
count="{{ $totalPengurus ?? 0 }}"
subtitle="Total responden terdata"
icon="user-check"
color="primary"
/>
</div>
<div class="w-25">
<x-summary-card
title="Koperasi Terdata"
count="{{ $totalKoperasi ?? 0 }}"
subtitle="Unit Koperasi yang terdata"
icon="map-pin"
color="success"
/>
</div>
<div class="w-25">
<x-summary-card
title="Jabatan/Peran"
count="{{ $totalJabatan ?? 0 }}"
subtitle="Jenis jabatan/peran terdata"
icon="briefcase"
color="info"
/>
</div>
</div>
<div class="card">
<div class="card-header border-bottom">
<h5 class="card-title mb-0">Data Pengurus Koperasi</h5>
</div>
<div class="card-body border-bottom pt-4">
<form method="GET" action="{{ route('koperasi.pengurus.index') }}" class="d-flex justify-content-between align-items-center row gap-3 gap-md-0">
<div class="col-md-auto">
<div class="d-flex align-items-center">
<label class="me-2 text-nowrap">Show</label>
<select name="per_page" class="form-select form-select-sm w-auto" onchange="this.form.submit()">
<option value="10" {{ request('per_page') == 10 ? 'selected' : '' }}>10</option>
<option value="25" {{ request('per_page') == 25 ? 'selected' : '' }}>25</option>
</select>
<label class="ms-2 text-nowrap">entries</label>
</div>
</div>
<div class="col-md-auto d-flex gap-2">
<div class="input-group input-group-sm w-auto">
<input type="text" name="search" class="form-control" placeholder="Cari Nama..." value="{{ request('search') }}">
<button type="submit" class="btn btn-outline-primary"><i class="ti tabler-search"></i></button>
</div>
<a href="{{ route('koperasi.pengurus.create') }}" class="btn btn-sm btn-primary">
<i class="ti tabler-plus me-1"></i> Tambah
</a>
</div>
</form>
</div>
<div class="table-responsive text-nowrap">
<table class="table table-hover">
<thead class="border-top">
<tr>
<th>No</th>
<th>Nama Pengurus</th>
<th>Posisi</th>
<th>Nama Koperasi</th>
<th>Jenis Koperasi</th>
<th>Skala Operasi</th>
@if($showEnumerator ?? true)
<th>Nama Enumerator</th>
@endif
<th>Aksi</th>
</tr>
</thead>
<tbody>
@forelse($pengurus ?? [] as $item)
<tr>
<td>{{ (($pengurus->firstItem() ?? 1) - 1) + $loop->iteration }}</td>
<td><span class="text-heading">{{ $item->nama_lengkap }}</span></td>
<td><span class="text-muted">{{ $item->posisi_dalam_koperasi }}</span></td>
<td><span class="text-muted">{{ $item->nama_koperasi }}</span></td>
<td><span class="text-muted">{{ $item->jenis_koperasi }}</span></td>
<td><span class="text-muted">{{ $item->skala_operasi }}</span></td>
@if($showEnumerator ?? true)
<td><span class="text-muted">{{ $item->creator?->name ?? '-' }}</span></td>
@endif
<td>
<div class="d-flex align-items-center">
<a href="{{ route('koperasi.pengurus.edit', $item->id) }}" class="btn btn-icon btn-text-secondary rounded-pill edit-record">
<i class="ti tabler-edit ti-md"></i>
</a>
<a href="javascript:;" class="btn btn-icon btn-text-secondary rounded-pill delete-record" data-id="{{ $item->id }}" data-nama="{{ $item->nama_lengkap }}">
<i class="ti tabler-trash ti-md"></i>
</a>
</div>
</td>
</tr>
@empty
<tr>
<td colspan="{{ ($showEnumerator ?? true) ? 8 : 7 }}" class="text-center py-4">Tidak ada data ditemukan.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
<div class="card-footer d-flex justify-content-center justify-content-md-end border-top">
@if(isset($pengurus) && $pengurus instanceof \Illuminate\Pagination\LengthAwarePaginator && $pengurus->hasPages())
{{ $pengurus->appends(request()->query())->links('pagination::bootstrap-5') }}
@endif
</div>
</div>
</div>
@endsection
@push('scripts')
<script>
'use strict';
const csrfToken = "{{ csrf_token() }}";
$(document).ready(function () {
$(document).on('click', '.delete-record', function () {
const id = $(this).data('id');
const name = $(this).data('nama');
Swal.fire({
title: 'Apakah Anda yakin?',
text: `Profil "${name}" akan dihapus secara permanen!`,
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Ya, Hapus!',
cancelButtonText: 'Batal',
customClass: { confirmButton: 'btn btn-primary me-3', cancelButton: 'btn btn-label-secondary' },
buttonsStyling: false
}).then(function (result) {
if (result.value) {
$.ajax({
url: '{{ route('koperasi.pengurus.destroy', ':id') }}'.replace(':id', id),
type: 'DELETE' ,
headers: { 'X-CSRF-TOKEN': csrfToken },
success: function (res) {
Swal.fire({ icon: 'success', title: 'Terhapus!', text: res.message, showConfirmButton: false, timer: 1500 })
.then(() => window.location.reload());
},
error: () => Swal.fire({ icon: 'error', title: 'Gagal!', text: 'Terjadi kesalahan sistem.' })
});
}
});
});
});
</script>
@endpush
@@ -0,0 +1,302 @@
@extends('layout.main')
@section('content')
<div class="container-xxl flex-grow-1 container-p-y">
@include('koperasi.profil.header', ['pengurus' => $pengurus])
<div class="card shadow-sm border-0 mb-4">
<div class="card-body p-0">
<form action="{{ route('koperasi.pengurus.survey.store', ['id' => $pengurus->id ?? 0, 'step' => 2]) }}" method="POST" class="p-4 p-md-5">
@csrf
<input type="hidden" name="pengurus_koperasi_id" value="{{ $pengurus->id ?? '' }}">
<h5 class="fw-bold border-bottom pb-2 mb-4">RANTAI NILAI & AKSES PASAR</h5>
<div class="alert alert-warning mb-4">Ditanyakan khusus untuk Koperasi yang melakukan kegiatan jual beli/agregasi.</div>
<h6 class="fw-bold text-dark mt-5">K2. Harga di Setiap Titik</h6>
<h5 class="pb-3 border-bottom mb-10 mt-4">Saluran Pembelian/Sumber Pasokan</h5>
<div class="mb-4">
<h6 class="form-label fw-semibold mb-2">Dari mana Koperasi biasanya memperoleh pasokan komoditas ini? (Boleh pilih >1)</h6>
<div class="form-check form-check-inline"><input class="form-check-input" type="checkbox" name="sumber_pasokan[]" value="Anggota" {{ str_contains($pengurus->sumber_pasokan ?? '', 'Anggota') ? 'checked' : '' }}><label class="form-check-label">Anggota kelompok tani langsung</label></div>
<div class="form-check form-check-inline"><input class="form-check-input" type="checkbox" name="sumber_pasokan[]" value="Petani non-anggota" {{ str_contains($pengurus->sumber_pasokan ?? '', 'Petani non-anggota') ? 'checked' : '' }}><label class="form-check-label">Petani non-anggota</label></div>
<div class="form-check form-check-inline"><input class="form-check-input" type="checkbox" name="sumber_pasokan[]" value="Pengumpul/sub-agen" {{ str_contains($pengurus->sumber_pasokan ?? '', 'Pengumpul/sub-agen') ? 'checked' : '' }}><label class="form-check-label">Pengumpul/sub-agen desa</label></div>
<div class="form-check form-check-inline"><input class="form-check-input" type="checkbox" name="sumber_pasokan[]" value="Campuran" {{ str_contains($pengurus->sumber_pasokan ?? '', 'Campuran') ? 'checked' : '' }}><label class="form-check-label">Campuran</label></div>
<div class="form-check d-flex align-items-center gap-2 mt-2">
<input class="form-check-input trigger-toggle-check" type="checkbox" name="sumber_pasokan[]" value="Lainnya" data-target="sumberLainnya" {{ str_contains($pengurus->sumber_pasokan ?? '', 'Lainnya') ? 'checked' : '' }}>
<label class="form-check-label text-nowrap">Lainnya:</label>
<input type="text" class="form-control form-control-sm w-auto {{ str_contains($pengurus->sumber_pasokan ?? '', 'Lainnya') ? '' : 'd-none' }}" id="sumberLainnya" name="sumber_pasokan_lainnya" value="{{ old('sumber_pasokan_lainnya', $pengurus->sumber_pasokan_lainnya) }}">
</div>
</div>
<div class="row mb-4">
<div class="col-md-4 mb-3">
<label class="form-label fw-semibold">Bentuk produk yang dibeli:</label>
<div class="form-check"><input class="form-check-input" type="checkbox" name="bentuk_pembelian[]" value="Buah segar hasil petik" {{ str_contains($pengurus->bentuk_pembelian ?? '', 'Buah segar hasil petik') ? 'checked' : '' }}><label class="form-check-label">Buah segar hasil petik</label></div>
<div class="form-check"><input class="form-check-input" type="checkbox" name="bentuk_pembelian[]" value="Biji basah" {{ str_contains($pengurus->bentuk_pembelian ?? '', 'Biji basah') ? 'checked' : '' }}><label class="form-check-label">Biji basah</label></div>
<div class="form-check"><input class="form-check-input" type="checkbox" name="bentuk_pembelian[]" value="Biji kering/kupas" {{ str_contains($pengurus->bentuk_pembelian ?? '', 'Biji kering/kupas') ? 'checked' : '' }}><label class="form-check-label">Biji kering/biji kupas</label></div>
<div class="form-check"><input class="form-check-input" type="checkbox" name="bentuk_pembelian[]" value="Produk terfermentasi" {{ str_contains($pengurus->bentuk_pembelian ?? '', 'Produk terfermentasi') ? 'checked' : '' }}><label class="form-check-label">Produk terfermentasi</label></div>
</div>
<div class="col-md-8 mb-3">
<label class="form-label fw-semibold">Estimasi volume yang dibeli (Kg):</label>
<div class="d-flex gap-2 mb-2">
<span class="input-group-text w-50">Panen terakhir</span>
<input type="number" class="form-control" name="volume_beli_sekarang" placeholder="kg" value="{{ old('volume_beli_sekarang', $pengurus->volume_beli_sekarang) }}">
</div>
<div class="d-flex gap-2 mb-2">
<span class="input-group-text w-50">Tahun lalu</span>
<input type="number" class="form-control" name="volume_beli_1_tahun" placeholder="kg" value="{{ old('volume_beli_1_tahun', $pengurus->volume_beli_1_tahun) }}">
</div>
<div class="d-flex gap-2">
<span class="input-group-text w-50">Dua tahun lalu</span>
<input type="number" class="form-control" name="volume_beli_2_tahun" placeholder="kg" value="{{ old('volume_beli_2_tahun', $pengurus->volume_beli_2_tahun) }}">
</div>
</div>
</div>
<h5 class="pb-3 border-bottom mb-10 mt-4">Saluran Penjualan Utama</h5>
<div class="mb-4">
<h6 class="form-label fw-semibold">Kepada siapa Koperasi biasanya menjual hasil yang dikumpulkan? (Boleh pilih >1)</h6>
<div class="row g-2">
<div class="col-md-6"><div class="form-check"><input class="form-check-input" type="checkbox" name="tujuan_penjualan[]" value="Offtaker/Eksportir" {{ str_contains($pengurus->tujuan_penjualan ?? '', 'Offtaker/Eksportir') ? 'checked' : '' }}><label class="form-check-label">Offtaker/eksportir langsung</label></div></div>
<div class="col-md-6"><div class="form-check"><input class="form-check-input" type="checkbox" name="tujuan_penjualan[]" value="Pedagang besar" {{ str_contains($pengurus->tujuan_penjualan ?? '', 'Pedagang besar') ? 'checked' : '' }}><label class="form-check-label">Pedagang besar/pengepul kabupaten</label></div></div>
<div class="col-md-6"><div class="form-check"><input class="form-check-input" type="checkbox" name="tujuan_penjualan[]" value="Koperasi/BUMDes lain" {{ str_contains($pengurus->tujuan_penjualan ?? '', 'Koperasi/BUMDes lain') ? 'checked' : '' }}><label class="form-check-label">Koperasi atau BUMDes lain</label></div></div>
<div class="col-md-6"><div class="form-check"><input class="form-check-input" type="checkbox" name="tujuan_penjualan[]" value="Eceran" {{ str_contains($pengurus->tujuan_penjualan ?? '', 'Eceran') ? 'checked' : '' }}><label class="form-check-label">Dijual eceran/pasar lokal</label></div></div>
<div class="col-md-6"><div class="form-check"><input class="form-check-input" type="checkbox" name="tujuan_penjualan[]" value="Belum pernah" {{ str_contains($pengurus->tujuan_penjualan ?? '', 'Belum pernah') ? 'checked' : '' }}><label class="form-check-label">Belum pernah (masih disimpan)</label></div></div>
<div class="col-md-6">
<div class="form-check d-flex align-items-center gap-2">
<input class="form-check-input trigger-toggle-check" type="checkbox" name="tujuan_penjualan[]" value="Lainnya" data-target="jualLainnya" {{ str_contains($pengurus->tujuan_penjualan ?? '', 'Lainnya') ? 'checked' : '' }}>
<label class="form-check-label text-nowrap">Lainnya:</label>
<input type="text" class="form-control form-control-sm w-75 {{ str_contains($pengurus->tujuan_penjualan ?? '', 'Lainnya') ? '' : 'd-none' }}" id="jualLainnya" name="tujuan_penjualan_lainnya" value="{{ old('tujuan_penjualan_lainnya', $pengurus->tujuan_penjualan_lainnya) }}">
</div>
</div>
</div>
</div>
<div class="row mb-4 border-bottom pb-4">
<div class="col-md-4 mb-3">
<label class="form-label fw-semibold">Bentuk produk yang dijual:</label>
<div class="form-check"><input class="form-check-input" type="checkbox" name="bentuk_penjualan[]" value="Buah segar hasil petik" {{ str_contains($pengurus->bentuk_penjualan ?? '', 'Buah segar hasil petik') ? 'checked' : '' }}><label class="form-check-label">Buah segar hasil petik</label></div>
<div class="form-check"><input class="form-check-input" type="checkbox" name="bentuk_penjualan[]" value="Biji basah" {{ str_contains($pengurus->bentuk_penjualan ?? '', 'Biji basah') ? 'checked' : '' }}><label class="form-check-label">Biji basah</label></div>
<div class="form-check"><input class="form-check-input" type="checkbox" name="bentuk_penjualan[]" value="Biji kering/kupas" {{ str_contains($pengurus->bentuk_penjualan ?? '', 'Biji kering/kupas') ? 'checked' : '' }}><label class="form-check-label">Biji kering/biji kupas</label></div>
<div class="form-check"><input class="form-check-input" type="checkbox" name="bentuk_penjualan[]" value="Produk terfermentasi" {{ str_contains($pengurus->bentuk_penjualan ?? '', 'Produk terfermentasi') ? 'checked' : '' }}><label class="form-check-label">Produk terfermentasi</label></div>
</div>
<div class="col-md-8 mb-3">
<label class="form-label fw-semibold">Estimasi volume yang terjual (Kg):</label>
<div class="d-flex gap-2 mb-2">
<span class="input-group-text w-50">Panen terakhir</span>
<input type="number" class="form-control" name="volume_jual_sekarang" placeholder="kg" value="{{ old('volume_jual_sekarang', $pengurus->volume_jual_sekarang) }}">
</div>
<div class="d-flex gap-2 mb-2">
<span class="input-group-text w-50">Tahun lalu</span>
<input type="number" class="form-control" name="volume_jual_1_tahun" placeholder="kg" value="{{ old('volume_jual_1_tahun', $pengurus->volume_jual_1_tahun) }}">
</div>
<div class="d-flex gap-2">
<span class="input-group-text w-50">Dua tahun lalu</span>
<input type="number" class="form-control" name="volume_jual_2_tahun" placeholder="kg" value="{{ old('volume_jual_2_tahun', $pengurus->volume_jual_2_tahun) }}">
</div>
</div>
</div>
<h5 class="pb-3 border-bottom mb-10 mt-4">Sifat Hubungan dengan Petani Pemasok</h5>
<div class="mb-4">
<label class="form-label fw-semibold">Sifat Hubungan dengan Petani Pemasok (Boleh pilih >1)</label>
<div class="form-check"><input class="form-check-input" type="checkbox" name="sifat_hubungan_bisnis[]" value="Bebas" {{ str_contains($pengurus->sifat_hubungan_bisnis ?? '', 'Bebas') ? 'checked' : '' }}><label class="form-check-label">Bebas/tidak terikat petani bebas menjual kapan saja tanpa ikatan</label></div>
<div class="form-check"><input class="form-check-input" type="checkbox" name="sifat_hubungan_bisnis[]" value="Langganan tetap" {{ str_contains($pengurus->sifat_hubungan_bisnis ?? '', 'Langganan tetap') ? 'checked' : '' }}><label class="form-check-label">Langganan tetap rutin menjual, tapi tanpa perjanjian formal</label></div>
<div class="form-check"><input class="form-check-input" type="checkbox" name="sifat_hubungan_bisnis[]" value="Berbasis hutang" {{ str_contains($pengurus->sifat_hubungan_bisnis ?? '', 'Berbasis hutang') ? 'checked' : '' }}><label class="form-check-label">Berbasis hutang/pinjaman menjual karena punya pinjaman</label></div>
<div class="form-check"><input class="form-check-input" type="checkbox" name="sifat_hubungan_bisnis[]" value="Titip jual" {{ str_contains($pengurus->sifat_hubungan_bisnis ?? '', 'Titip jual') ? 'checked' : '' }}><label class="form-check-label">Titip jual (konsinyasi) petani dibayar setelah hasil terjual</label></div>
<div class="form-check"><input class="form-check-input" type="checkbox" name="sifat_hubungan_bisnis[]" value="Komitmen volume" {{ str_contains($pengurus->sifat_hubungan_bisnis ?? '', 'Komitmen volume') ? 'checked' : '' }}><label class="form-check-label">Ada komitmen volume kesepakatan volume berkala</label></div>
<div class="form-check"><input class="form-check-input" type="checkbox" name="sifat_hubungan_bisnis[]" value="Semi-kontrak" {{ str_contains($pengurus->sifat_hubungan_bisnis ?? '', 'Semi-kontrak') ? 'checked' : '' }}><label class="form-check-label">Semi-kontrak kesepakatan harga, volume, & waktu</label></div>
<div class="form-check d-flex align-items-center gap-2 mt-2">
<input class="form-check-input trigger-toggle-check" type="checkbox" name="sifat_hubungan_bisnis[]" value="Lainnya" data-target="sifatLainnya" {{ str_contains($pengurus->sifat_hubungan_bisnis ?? '', 'Lainnya') ? 'checked' : '' }}>
<label class="form-check-label text-nowrap">Lainnya:</label>
<input type="text" class="form-control form-control-sm w-50 {{ str_contains($pengurus->sifat_hubungan_bisnis ?? '', 'Lainnya') ? '' : 'd-none' }}" id="sifatLainnya" name="sifat_hubungan_lainnya" value="{{ old('sifat_hubungan_lainnya', $pengurus->sifat_hubungan_lainnya) }}">
</div>
</div>
<div class="row border-bottom mb-4 mt-4">
<div class="col-md-6 mb-3">
<label class="form-label fw-semibold">Mekanisme pembayaran ke petani?</label>
<div class="form-check"><input class="form-check-input" type="radio" name="mekanisme_pembayaran" value="Tunai" {{ old('mekanisme_pembayaran', $pengurus->mekanisme_pembayaran) == 'Tunai' ? 'checked' : '' }}><label class="form-check-label">Tunai langsung</label></div>
<div class="form-check"><input class="form-check-input" type="radio" name="mekanisme_pembayaran" value="Tempo" {{ old('mekanisme_pembayaran', $pengurus->mekanisme_pembayaran) == 'Tempo' ? 'checked' : '' }}><label class="form-check-label">Sistem tempo/dibayar kemudian</label></div>
<div class="form-check"><input class="form-check-input" type="radio" name="mekanisme_pembayaran" value="Campuran" {{ old('mekanisme_pembayaran', $pengurus->mekanisme_pembayaran) == 'Campuran' ? 'checked' : '' }}><label class="form-check-label">Campuran, tergantung situasi</label></div>
</div>
<div class="col-md-6 mb-3">
<label class="form-label fw-semibold">Apakah ada perjanjian/kontrak sebelum pembelian?</label>
<div class="form-check"><input class="form-check-input" type="radio" name="status_perjanjian_tertulis" value="Tertulis" {{ old('status_perjanjian_tertulis', $pengurus->status_perjanjian_tertulis) == 'Tertulis' ? 'checked' : '' }}><label class="form-check-label">Ada, tertulis</label></div>
<div class="form-check"><input class="form-check-input" type="radio" name="status_perjanjian_tertulis" value="Lisan" {{ old('status_perjanjian_tertulis', $pengurus->status_perjanjian_tertulis) == 'Lisan' ? 'checked' : '' }}><label class="form-check-label">Ada, kesepakatan lisan saja</label></div>
<div class="form-check"><input class="form-check-input" type="radio" name="status_perjanjian_tertulis" value="Tidak ada" {{ old('status_perjanjian_tertulis', $pengurus->status_perjanjian_tertulis) == 'Tidak ada' ? 'checked' : '' }}><label class="form-check-label">Tidak ada perjanjian</label></div>
</div>
</div>
<h6 class="fw-bold text-dark mt-5">K2. Harga di Setiap Titik</h6>
<div class="mb-4">
<h5 class="pb-3 border-bottom mb-10 mt-4">Harga Beli Koperasi dari Petani (Rp/kg)</h5>
<div class="table-responsive">
<table class="table table-sm align-middle">
<thead class="table-primary">
<tr>
<th style="border-top-left-radius: 8px; border-bottom-left-radius: 8px; border: none;">BENTUK PRODUK</th>
<th style="border: none;">PANEN TERAKHIR</th>
<th style="border: none;">TAHUN LALU</th>
<th style="border-top-right-radius: 8px; border-bottom-right-radius: 8px; border: none;">DUA TAHUN LALU</th>
</tr>
</thead>
<tbody>
<tr>
<td>Buah segar petik</td>
<td><div class="input-group input-group-sm"><span class="input-group-text">Rp</span><input type="number" class="form-control" name="harga_segar_sekarang" value="{{ old('harga_segar_sekarang', $pengurus->harga_segar_sekarang) }}"></div></td>
<td><div class="input-group input-group-sm"><span class="input-group-text">Rp</span><input type="number" class="form-control" name="harga_segar_1_tahun" value="{{ old('harga_segar_1_tahun', $pengurus->harga_segar_1_tahun) }}"></div></td>
<td><div class="input-group input-group-sm"><span class="input-group-text">Rp</span><input type="number" class="form-control" name="harga_segar_2_tahun" value="{{ old('harga_segar_2_tahun', $pengurus->harga_segar_2_tahun) }}"></div></td>
</tr>
<tr>
<td>Biji basah</td>
<td><div class="input-group input-group-sm"><span class="input-group-text">Rp</span><input type="number" class="form-control" name="harga_basah_sekarang" value="{{ old('harga_basah_sekarang', $pengurus->harga_basah_sekarang) }}"></div></td>
<td><div class="input-group input-group-sm"><span class="input-group-text">Rp</span><input type="number" class="form-control" name="harga_basah_1_tahun" value="{{ old('harga_basah_1_tahun', $pengurus->harga_basah_1_tahun) }}"></div></td>
<td><div class="input-group input-group-sm"><span class="input-group-text">Rp</span><input type="number" class="form-control" name="harga_basah_2_tahun" value="{{ old('harga_basah_2_tahun', $pengurus->harga_basah_2_tahun) }}"></div></td>
</tr>
<tr>
<td>Biji kering/kupas</td>
<td><div class="input-group input-group-sm"><span class="input-group-text">Rp</span><input type="number" class="form-control" name="harga_kering_sekarang" value="{{ old('harga_kering_sekarang', $pengurus->harga_kering_sekarang) }}"></div></td>
<td><div class="input-group input-group-sm"><span class="input-group-text">Rp</span><input type="number" class="form-control" name="harga_kering_1_tahun" value="{{ old('harga_kering_1_tahun', $pengurus->harga_kering_1_tahun) }}"></div></td>
<td><div class="input-group input-group-sm"><span class="input-group-text">Rp</span><input type="number" class="form-control" name="harga_kering_2_tahun" value="{{ old('harga_kering_2_tahun', $pengurus->harga_kering_2_tahun) }}"></div></td>
</tr>
<tr>
<td>Terfermentasi</td>
<td><div class="input-group input-group-sm"><span class="input-group-text">Rp</span><input type="number" class="form-control" name="harga_fermentasi_sekarang" value="{{ old('harga_fermentasi_sekarang', $pengurus->harga_fermentasi_sekarang) }}"></div></td>
<td><div class="input-group input-group-sm"><span class="input-group-text">Rp</span><input type="number" class="form-control" name="harga_fermentasi_1_tahun" value="{{ old('harga_fermentasi_1_tahun', $pengurus->harga_fermentasi_1_tahun) }}"></div></td>
<td><div class="input-group input-group-sm"><span class="input-group-text">Rp</span><input type="number" class="form-control" name="harga_fermentasi_2_tahun" value="{{ old('harga_fermentasi_2_tahun', $pengurus->harga_fermentasi_2_tahun) }}"></div></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="mb-4">
<label class="form-label fw-semibold">Bagaimana cara Koperasi menetapkan harga beli dari petani?</label>
<div class="form-check"><input class="form-check-input" type="checkbox" name="cara_menetapkan_harga_pasar[]" value="Ikut pasar" {{ str_contains($pengurus->cara_menetapkan_harga_pasar ?? '', 'Ikut pasar') ? 'checked' : '' }}><label class="form-check-label">Mengikuti harga pasar/tengkulak setempat</label></div>
<div class="form-check"><input class="form-check-input" type="checkbox" name="cara_menetapkan_harga_pasar[]" value="Kalkulasi biaya" {{ str_contains($pengurus->cara_menetapkan_harga_pasar ?? '', 'Kalkulasi biaya') ? 'checked' : '' }}><label class="form-check-label">Ditetapkan sendiri berdasarkan kalkulasi biaya</label></div>
<div class="form-check"><input class="form-check-input" type="checkbox" name="cara_menetapkan_harga_pasar[]" value="Harga offtaker" {{ str_contains($pengurus->cara_menetapkan_harga_pasar ?? '', 'Harga offtaker') ? 'checked' : '' }}><label class="form-check-label">Mengikuti harga dari offtaker/mitra</label></div>
<div class="form-check d-inline-flex align-items-center gap-2 mt-2">
<input class="form-check-input trigger-toggle-check" type="checkbox" name="cara_menetapkan_harga_pasar[]" value="Lainnya" data-target="hargaLainnya" {{ str_contains($pengurus->cara_menetapkan_harga_pasar ?? '', 'Lainnya') ? 'checked' : '' }}>
<label class="form-check-label text-nowrap">Lainnya:</label>
<input type="text" class="form-control form-control-sm w-auto {{ str_contains($pengurus->cara_menetapkan_harga_pasar ?? '', 'Lainnya') ? '' : 'd-none' }}" id="hargaLainnya" name="cara_menetapkan_harga_lainnya" value="{{ old('cara_menetapkan_harga_lainnya', $pengurus->cara_menetapkan_harga_lainnya) }}">
</div>
</div>
<div id="blok-k3" class="mt-5 pt-3 border-top border-primary">
<h6 class="fw-bold text-primary mb-4"><i></i>K3. Peluang Nilai Tambah (Pengolahan Pascapanen)</h6>
<h5 class="pb-3 border-bottom mb-4 mt-4">Sortasi & Pemilihan Kualitas</h5>
<div class="mb-4">
<label class="form-label fw-semibold">Apakah Koperasi melakukan sortasi/pemilihan kualitas hasil panen sebelum dijual?</label> <br>
<div class="form-check form-check-inline"><input class="form-check-input trigger-toggle" type="radio" name="praktik_sortasi" value="Ya rutin" data-target="sortasiInput" data-action="show" {{ old('praktik_sortasi', $pengurus->praktik_sortasi) == 'Ya rutin' ? 'checked' : '' }}><label class="form-check-label">Ya, rutin berstandar</label></div>
<div class="form-check form-check-inline"><input class="form-check-input trigger-toggle" type="radio" name="praktik_sortasi" value="Ya tidak konsisten" data-target="sortasiInput" data-action="show" {{ old('praktik_sortasi', $pengurus->praktik_sortasi) == 'Ya tidak konsisten' ? 'checked' : '' }}><label class="form-check-label">Ya, tidak konsisten</label></div>
<div class="form-check form-check-inline"><input class="form-check-input trigger-toggle" type="radio" name="praktik_sortasi" value="Belum pernah" data-target="sortasiInput" data-action="hide" {{ old('praktik_sortasi', $pengurus->praktik_sortasi) == 'Belum pernah' ? 'checked' : '' }}><label class="form-check-label">Belum pernah</label></div>
<!-- Blok ini dinamis hapus d-none jika Ya rutin / Ya tidak konsisten -->
<div class="mt-2 {{ in_array(old('praktik_sortasi', $pengurus->praktik_sortasi), ['Ya rutin', 'Ya tidak konsisten']) ? '' : 'd-none' }}" id="sortasiInput">
<label class="form-label small text-muted mb-1">Jika ya, standar/kriteria sortasi yang digunakan:</label>
<input type="text" class="form-control form-control-sm w-75" name="standar_sortasi" placeholder="Contoh: Ukuran biji, kadar air..." value="{{ old('standar_sortasi', $pengurus->standar_sortasi) }}">
</div>
</div>
<h5 class="pb-3 border-bottom mb-4 mt-4">Praktik Pengeringan</h5>
<div class="mb-4">
<label class="form-label fw-semibold">Apakah Koperasi melakukan pengeringan hasil panen?</label> <br>
<div class="form-check form-check-inline"><input class="form-check-input trigger-toggle" type="radio" name="praktik_pengeringan" value="Ya rutin" data-target="keringInput" data-action="show" {{ old('praktik_pengeringan', $pengurus->praktik_pengeringan) == 'Ya rutin' ? 'checked' : '' }}><label class="form-check-label">Ya, rutin</label></div>
<div class="form-check form-check-inline"><input class="form-check-input trigger-toggle" type="radio" name="praktik_pengeringan" value="Kadang" data-target="keringInput" data-action="show" {{ old('praktik_pengeringan', $pengurus->praktik_pengeringan) == 'Kadang' ? 'checked' : '' }}><label class="form-check-label">Kadang-kadang</label></div>
<div class="form-check form-check-inline"><input class="form-check-input trigger-toggle" type="radio" name="praktik_pengeringan" value="Tidak" data-target="keringInput" data-action="hide" {{ old('praktik_pengeringan', $pengurus->praktik_pengeringan) == 'Tidak' ? 'checked' : '' }}><label class="form-check-label">Tidak pernah</label></div>
<div class="mt-2 {{ in_array(old('praktik_pengeringan', $pengurus->praktik_pengeringan), ['Ya rutin', 'Kadang']) ? '' : 'd-none' }}" id="keringInput">
<label class="form-label small text-muted mb-1">Jika ya, cara dan tempat pengeringan:</label>
<input type="text" class="form-control form-control-sm w-75" name="cara_pengeringan" placeholder="Contoh: Jemur matahari di lantai jemur..." value="{{ old('cara_pengeringan', $pengurus->cara_pengeringan) }}">
</div>
</div>
<h5 class="pb-3 border-bottom mb-4 mt-4">Praktik Fermentasi</h5>
<div class="mb-4">
<label class="form-label fw-semibold">Apakah Koperasi melakukan/memfasilitasi fermentasi/pengepresan?</label> <br>
<div class="form-check form-check-inline"><input class="form-check-input trigger-toggle" type="radio" name="praktik_fermentasi" value="Ya rutin" data-target="fermInput" data-action="show" {{ old('praktik_fermentasi', $pengurus->praktik_fermentasi) == 'Ya rutin' ? 'checked' : '' }}><label class="form-check-label">Ya, rutin</label></div>
<div class="form-check form-check-inline"><input class="form-check-input trigger-toggle" type="radio" name="praktik_fermentasi" value="Pernah coba" data-target="fermInput" data-action="show" {{ old('praktik_fermentasi', $pengurus->praktik_fermentasi) == 'Pernah coba' ? 'checked' : '' }}><label class="form-check-label">Pernah mencoba</label></div>
<div class="form-check form-check-inline"><input class="form-check-input trigger-toggle" type="radio" name="praktik_fermentasi" value="Belum, direncakan" data-target="fermInput" data-action="hide" {{ old('praktik_fermentasi', $pengurus->praktik_fermentasi) == 'Belum, direncakan' ? 'checked' : '' }}><label class="form-check-label">Belum pernah (direncanakan)</label></div>
<div class="form-check form-check-inline"><input class="form-check-input trigger-toggle" type="radio" name="praktik_fermentasi" value="Belum, tidak" data-target="fermInput" data-action="hide" {{ old('praktik_fermentasi', $pengurus->praktik_fermentasi) == 'Belum, tidak' ? 'checked' : '' }}><label class="form-check-label">Belum pernah (tidak direncanakan)</label></div>
<div class="mt-3 p-3 bg-light rounded {{ in_array(old('praktik_fermentasi', $pengurus->praktik_fermentasi), ['Ya rutin', 'Pernah coba']) ? '' : 'd-none' }}" id="fermInput">
<label class="form-label small fw-bold mb-2">Model fermentasi (Boleh pilih >1):</label>
<div class="form-check form-check-inline"><input class="form-check-input" type="checkbox" name="model_fermentasi[]" value="Individual" {{ str_contains($pengurus->model_fermentasi ?? '', 'Individual') ? 'checked' : '' }}><label class="form-check-label">Individual</label></div>
<div class="form-check form-check-inline"><input class="form-check-input" type="checkbox" name="model_fermentasi[]" value="Kolektif" {{ str_contains($pengurus->model_fermentasi ?? '', 'Kolektif') ? 'checked' : '' }}><label class="form-check-label">Kolektif kelompok</label></div>
<div class="form-check form-check-inline"><input class="form-check-input" type="checkbox" name="model_fermentasi[]" value="Unit desa" {{ str_contains($pengurus->model_fermentasi ?? '', 'Unit desa') ? 'checked' : '' }}><label class="form-check-label">Unit pengolah desa</label></div>
<div class="form-check form-check-inline"><input class="form-check-input" type="checkbox" name="model_fermentasi[]" value="Campuran" {{ str_contains($pengurus->model_fermentasi ?? '', 'Campuran') ? 'checked' : '' }}><label class="form-check-label">Campuran</label></div>
<br>
<label class="form-label small fw-bold mt-3 mb-1">Bagaimana prosesnya dilakukan:</label>
<textarea class="form-control form-control-sm" name="proses_fermentasi" rows="2">{{ old('proses_fermentasi', $pengurus->proses_fermentasi) }}</textarea>
</div>
</div>
<div class="mb-4">
<h5 class="pb-3 border-bottom mb-4 mt-4">Besaran Premium Harga</h5>
<p class="small text-muted mb-2">Menurut pengalaman/pengamatan Koperasi, berapa kenaikan harga yang dapat dicapai jika sortasi/pengeringan/fermentasi standar diterapkan?</p>
<div class="d-flex align-items-center gap-3">
<div class="input-group input-group-sm w-auto">
<span class="input-group-text">Rp</span>
<input type="number" class="form-control" name="harga_premium_rupiah" placeholder="0" value="{{ old('harga_premium_rupiah', $pengurus->harga_premium_rupiah) }}">
<span class="input-group-text">/kg</span>
</div>
<span class="fw-bold text-muted">ATAU</span>
<div class="input-group input-group-sm w-auto">
<input type="number" class="form-control" name="harga_premium_persen" placeholder="0" max="100" value="{{ old('harga_premium_persen', $pengurus->harga_premium_persen) }}">
<span class="input-group-text">%</span>
</div>
</div>
</div>
</div>
<div class="d-flex justify-content-between mt-5 border-top pt-4">
<a href="{{ route('koperasi.pengurus.survey', ['id' => $pengurus->id ?? 0, 'step' => 1]) }}" class="btn btn-label-secondary">
<i class="ti tabler-arrow-left me-1"></i> Sebelumnya
</a>
<div class="ms-auto">
<button type="submit" class="btn btn-primary">
Simpan & Selanjutnya <i class="ti tabler-arrow-right ms-1"></i>
</button>
</div>
</div>
</form>
</div>
</div>
</div>
@endsection
@push('scripts')
<script>
$('.trigger-toggle-check').change(function() {
let target = $(this).data('target');
if ($(this).is(':checked')) {
$('#' + target).removeClass('d-none').focus();
} else {
$('#' + target).addClass('d-none').val('');
}
});
$('.trigger-toggle').change(function() {
let target = $(this).data('target');
let action = $(this).data('action');
if (action === 'show') {
$('#' + target).removeClass('d-none').focus();
} else {
$('#' + target).addClass('d-none').val('');
}
});
</script>
@endpush
@@ -0,0 +1,153 @@
@extends('layout.main')
@section('content')
<div class="container-xxl flex-grow-1 container-p-y">
@include('koperasi.profil.header', ['pengurus' => $pengurus])
<div class="card shadow-sm border-0 mb-4">
<div class="card-body p-0">
<form action="{{ route('koperasi.pengurus.survey.store', ['id' => $pengurus->id ?? 0, 'step' => 3]) }}" method="POST" class="p-4 p-md-5">
@csrf
<input type="hidden" name="pengurus_koperasi_id" value="{{ $pengurus->id ?? '' }}">
<h5 class="fw-bold text-dark mb-4">SOSIAL-EKONOMI RUMAH TANGGA</h5>
<div class="border-bottom pb-4 mb-4">
<h6 class="text-muted fw-normal mb-1">bagian tentang kendala & tantangan</h6>
<p class="text-muted small mb-0">Kuesioner bagian ini ditujukan untuk memetakan kestabilan finansial anggota koperasi yang bersangkutan</p>
</div>
<h6 class="fw-bold text-dark mb-3">K2 KONDISI EKONOMI RUMAH TANGGA</h6>
<h6 class="fw-semibold text-dark mb-4">Stabilitas penghasilan sepanjang tahun</h6>
<div class="row mb-4">
<div class="col-md-6 mb-3">
<label class="form-label">Berapa nominal simpanan wajib terbesar (secara perorangan) yang disetorkan oleh anggota koperasi ini?</label>
<div class="input-group">
<span class="input-group-text">Rp.</span>
<input type="number" class="form-control" name="simpanan_terbesar" placeholder="0" value="{{ old('simpanan_terbesar', $pengurus->simpanan_terbesar) }}">
</div>
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Pada bulan dan momen apa, simpanan wajib anggota koperasi secara rata-rata lebih tinggi, daripada setoran pada bulan lainnya?</label>
<input type="text" class="form-control" name="momen_simpanan_terbesar" placeholder="..." value="{{ old('momen_simpanan_terbesar', $pengurus->momen_simpanan_terbesar) }}">
</div>
</div>
<div id="blok-alasan-pinjaman" class="mb-4 p-3 bg-label-warning rounded border border-warning">
<label class="form-label fw-semibold text-dark">
<i class="ti tabler-bulb text-warning me-1"></i>(Tanyakan jika koperasi merupakan koperasi simpan pinjam) Apa alasan/keperluan utama anggota melakukan pinjaman kepada koperasi?
</label>
<div class="row g-2 mt-1">
<div class="col-md-4"><div class="form-check"><input class="form-check-input" type="checkbox" name="alasan_pinjaman[]" value="Produksi" {{ str_contains($pengurus->alasan_pinjaman ?? '', 'Produksi') ? 'checked' : '' }}><label class="form-check-label">Keperluan produksi</label></div></div>
<div class="col-md-4"><div class="form-check"><input class="form-check-input" type="checkbox" name="alasan_pinjaman[]" value="Pendidikan" {{ str_contains($pengurus->alasan_pinjaman ?? '', 'Pendidikan') ? 'checked' : '' }}><label class="form-check-label">Pendidikan</label></div></div>
<div class="col-md-4"><div class="form-check"><input class="form-check-input" type="checkbox" name="alasan_pinjaman[]" value="Kesehatan" {{ str_contains($pengurus->alasan_pinjaman ?? '', 'Kesehatan') ? 'checked' : '' }}><label class="form-check-label">Kesehatan</label></div></div>
<div class="col-md-4"><div class="form-check"><input class="form-check-input" type="checkbox" name="alasan_pinjaman[]" value="Acara keluarga" {{ str_contains($pengurus->alasan_pinjaman ?? '', 'Acara keluarga') ? 'checked' : '' }}><label class="form-check-label">Acara keluarga/adat/hari besar</label></div></div>
<div class="col-md-4"><div class="form-check"><input class="form-check-input" type="checkbox" name="alasan_pinjaman[]" value="Renovasi" {{ str_contains($pengurus->alasan_pinjaman ?? '', 'Renovasi') ? 'checked' : '' }}><label class="form-check-label">Renovasi rumah</label></div></div>
<div class="col-md-4"><div class="form-check"><input class="form-check-input" type="checkbox" name="alasan_pinjaman[]" value="Sekunder" {{ str_contains($pengurus->alasan_pinjaman ?? '', 'Sekunder') ? 'checked' : '' }}><label class="form-check-label">Kebutuhan sekunder</label></div></div>
<div class="col-12 mt-2">
<div class="form-check d-inline-flex align-items-center gap-2">
<input class="form-check-input trigger-toggle-check" type="checkbox" name="alasan_pinjaman[]" value="Lainnya" data-target="alasanLainnya" {{ str_contains($pengurus->alasan_pinjaman ?? '', 'Lainnya') ? 'checked' : '' }}>
<label class="form-check-label text-nowrap">Lainnya, sebutkan:</label>
<input type="text" class="form-control form-control-sm {{ str_contains($pengurus->alasan_pinjaman ?? '', 'Lainnya') ? '' : 'd-none' }}" id="alasanLainnya" name="alasan_pinjaman_lainnya" value="{{ old('alasan_pinjaman_lainnya', $pengurus->alasan_pinjaman_lainnya) }}">
</div>
</div>
</div>
</div>
<div id="blok-ikatan-keuangan" class="mt-5">
<h6 class="fw-bold text-dark mb-3">K6 IKATAN KEUANGAN</h6>
<h6 class="fw-semibold text-dark mb-3">Keterlibatan Koperasi Simpan Pinjam dengan Petani Anggota Koperasi</h6>
<div class="alert alert-outline-warning small py-2 mb-4">
<i class="ti tabler-bulb text-warning me-1"></i> Tanyakan jika koperasi merupakan koperasi simpan pinjam<br>
<i class="ti tabler-bulb text-warning me-1"></i> Pertanyaan pada bagian ini sensitif untuk sebagian orang, enumerator perlu melakukan strategi penggalian data tertentu kepada responden yang diwawancarai
</div>
<div class="mb-4">
<label class="form-label">Apakah terdapat mekanisme pembayaran pinjaman oleh petani anggota koperasi, dalam bentuk selain uang, misalnya hasil bumi?</label>
<div class="form-check form-check-inline"><input class="form-check-input trigger-toggle" type="radio" name="pembayaran_selain_uang" value="Ada" data-target="bentukBayarBlok" data-action="show" {{ old('pembayaran_selain_uang', $pengurus->pembayaran_selain_uang) == 'Ada' ? 'checked' : '' }}><label class="form-check-label">Ada</label></div>
<div class="form-check form-check-inline"><input class="form-check-input trigger-toggle" type="radio" name="pembayaran_selain_uang" value="Tidak ada" data-target="bentukBayarBlok" data-action="hide" {{ old('pembayaran_selain_uang', $pengurus->pembayaran_selain_uang) == 'Tidak ada' ? 'checked' : '' }}><label class="form-check-label">Tidak ada</label></div>
</div>
<div id="bentukBayarBlok" class="p-3 bg-light rounded {{ old('pembayaran_selain_uang', $pengurus->pembayaran_selain_uang) == 'Ada' ? '' : 'd-none' }}">
<div class="mb-4">
<label class="form-label">Jika Ada, apa bentuk pembayaran oleh petani anggota, yang dapat diterima koperasi selain dalam bentuk uang?</label>
<div class="form-check">
<input class="form-check-input trigger-hasil-bumi" type="checkbox" name="bentuk_pembayaran_barang[]" id="chkHasilBumi" value="Hasil bumi" {{ str_contains($pengurus->bentuk_pembayaran_barang ?? '', 'Hasil bumi') ? 'checked' : '' }}>
<label class="form-check-label" for="chkHasilBumi">Hasil bumi (hasil panen)</label>
</div>
<div class="form-check"><input class="form-check-input" type="checkbox" name="bentuk_pembayaran_barang[]" value="Ganti jasa" {{ str_contains($pengurus->bentuk_pembayaran_barang ?? '', 'Ganti jasa') ? 'checked' : '' }}><label class="form-check-label">Ganti jasa/tenaga harian</label></div>
<div class="form-check d-flex align-items-center gap-2 mt-2">
<input class="form-check-input trigger-toggle-check" type="checkbox" name="bentuk_pembayaran_barang[]" value="Lainnya" data-target="bentukBayarLainnya" {{ str_contains($pengurus->bentuk_pembayaran_barang ?? '', 'Lainnya') ? 'checked' : '' }}>
<label class="form-check-label text-nowrap">Lainnya, sebutkan:</label>
<input type="text" class="form-control form-control-sm w-50 {{ str_contains($pengurus->bentuk_pembayaran_barang ?? '', 'Lainnya') ? '' : 'd-none' }}" id="bentukBayarLainnya" name="bentuk_pembayaran_lainnya" value="{{ old('bentuk_pembayaran_lainnya', $pengurus->bentuk_pembayaran_lainnya) }}">
</div>
</div>
<div class="mb-2 {{ str_contains($pengurus->bentuk_pembayaran_barang ?? '', 'Hasil bumi') ? '' : 'd-none' }}" id="blokHitungPasar">
<label class="form-label">Jika pembayaran pinjaman dilakukan dalam bentuk penukaran hasil bumi, apakah hasil bumi tersebut dihitung berdasarkan harga yang berlaku di pasaran?</label><br>
<div class="form-check form-check-inline"><input class="form-check-input trigger-toggle" type="radio" name="perhitungan_harga_pasar" value="Ya" data-target="alasanPasar" data-action="hide" {{ old('perhitungan_harga_pasar', $pengurus->perhitungan_harga_pasar) == 'Ya' ? 'checked' : '' }}><label class="form-check-label">Ya</label></div>
<div class="form-check form-check-inline d-inline-flex align-items-center gap-2">
<input class="form-check-input trigger-toggle" type="radio" name="perhitungan_harga_pasar" value="Tidak" data-target="alasanPasar" data-action="show" {{ old('perhitungan_harga_pasar', $pengurus->perhitungan_harga_pasar) == 'Tidak' ? 'checked' : '' }}>
<label class="form-check-label text-nowrap">Tidak, sebutkan alasannya:</label>
<input type="text" class="form-control form-control-sm {{ old('perhitungan_harga_pasar', $pengurus->perhitungan_harga_pasar) == 'Tidak' ? '' : 'd-none' }}" id="alasanPasar" name="alasan_bukan_harga_pasar" value="{{ old('alasan_bukan_harga_pasar', $pengurus->alasan_bukan_harga_pasar) }}">
</div>
</div>
</div>
</div>
<div class="d-flex justify-content-between mt-5 border-top pt-4">
<a href="{{ route('koperasi.pengurus.survey', ['id' => $pengurus->id ?? 0, 'step' => 2]) }}" class="btn btn-label-secondary">
<i class="ti tabler-arrow-left me-1"></i> Sebelumnya
</a>
<div class="ms-auto">
<button type="submit" class="btn btn-success">
<i class="ti tabler-device-floppy me-1"></i> Selesaikan Survei
</button>
</div>
</div>
</form>
</div>
</div>
</div>
@endsection
@push('scripts')
<script>
$('.trigger-toggle-check').change(function() {
let target = $(this).data('target');
if ($(this).is(':checked')) {
$('#' + target).removeClass('d-none').focus();
} else {
$('#' + target).addClass('d-none').val('');
}
});
$('.trigger-toggle').change(function() {
let target = $(this).data('target');
let action = $(this).data('action');
if (action === 'show') {
$('#' + target).removeClass('d-none').focus();
} else {
$('#' + target).addClass('d-none').val('');
}
});
$(document).ready(function() {
$('.trigger-hasil-bumi').on('change', function() {
if ($(this).is(':checked')) {
$('#blokHitungPasar').removeClass('d-none').hide().slideDown();
} else {
$('#blokHitungPasar').slideUp(function() {
$(this).addClass('d-none');
$('input[name="perhitungan_harga_pasar"]').prop('checked', false);
$('#alasanPasar').addClass('d-none').val('');
});
}
});
});
</script>
@endpush
+1 -1
View File
@@ -7,7 +7,7 @@
<script>
document.write(new Date().getFullYear());
</script>
, by <a href="#" target="_blank" class="footer-link">Yayasan Bhumi Loka Lestari</a>
, by <a href="#" target="_blank" class="footer-link">PT Republik Solusi</a>
</div>
</div>
</div>
+24 -4
View File
@@ -15,7 +15,7 @@
name="viewport"
content="width=device-width, initial-scale=1.0, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0" />
<meta name="robots" content="noindex, nofollow" />
<title>AgroMonev | {{ $title ?? 'Database Petani' }}</title>
<title>AGERA | {{ $title ?? 'Database Petani' }}</title>
<meta name="description" content="" />
@@ -26,7 +26,7 @@
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<link rel="icon" type="image/x-icon" href="../../assets/img/favicon/favicon.ico" />
<link rel="icon" type="image/svg+xml" href="{{ asset('assets/img/logo/agera_icon.svg') }}" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
@@ -43,7 +43,7 @@
<link rel="stylesheet" href="{{ asset('assets/vendor/libs/pickr/pickr-themes.css') }}" />
<link rel="stylesheet" href="{{ asset('assets/vendor/css/core.css') }}" />
<link rel="stylesheet" href="{{ asset('assets/css/demo.css') }}" />
<link rel="stylesheet" href="{{ asset('assets/css/demo.css') }}?v={{ filemtime(public_path('assets/css/demo.css')) }}" />
<link rel="stylesheet" href="{{ asset('assets/vendor/libs/perfect-scrollbar/perfect-scrollbar.css') }}" />
@@ -64,12 +64,32 @@
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css">
<style>
.agera-logo {
width: 200px;
height: auto;
max-height: 78px;
object-fit: contain;
display: block;
}
.app-brand-logo.demo {
width: auto;
height: auto;
}
.app-brand.demo {
height: 98px;
padding-top: 10px;
padding-bottom: 10px;
}
</style>
@stack('style')
<script src="{{ asset('assets/vendor/js/helpers.js') }}"></script>
<script src="{{ asset('assets/vendor/js/template-customizer.js') }}"></script>
<script src="{{ asset('assets/js/config.js') }}"></script>
<script src="{{ asset('assets/js/config.js') }}?v={{ filemtime(public_path('assets/js/config.js')) }}"></script>
@stack('scripts-head')
</head>
+138 -183
View File
@@ -2,38 +2,11 @@
<div class="app-brand demo ">
<a href="{{ route('home') }}" class="app-brand-link">
<span class="app-brand-logo demo">
<span>
<!-- AgroMonev -->
<svg width="32" height="24" viewBox="0 0 32 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.3" d="M16 2.5C16 2.5 5 6.5 5 15.5C5 20.5 11 23.5 16 23.5V2.5Z" fill="#28a745" />
<path opacity="0.6" d="M16 2.5C16 2.5 27 6.5 27 15.5C27 20.5 21 23.5 16 23.5V2.5Z" fill="#28a745" />
<path d="M2 19L10 11L15 15" stroke="#0054A6" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" />
<path d="M15 15L27 4" stroke="#ED1C24" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" />
<circle cx="27" cy="4" r="2.5" fill="#ED1C24" />
</svg>
<!-- AgroPro -->
<!-- <svg width="32" height="24" viewBox="0 0 32 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.25" d="M16 2.5C16 2.5 5 6.5 5 15.5C5 20.5 11 23.5 16 23.5V2.5Z" fill="#28A745"/>
<path opacity="0.5" d="M16 2.5C16 2.5 27 6.5 27 15.5C27 20.5 21 23.5 16 23.5V2.5Z" fill="#28A745"/>
<rect x="5.5" y="5.5" width="21" height="15" rx="2.5" fill="white" stroke="#0054A6" stroke-width="1.5"/>
<rect x="11" y="3.5" width="10" height="4" rx="2" fill="#0054A6"/>
<circle cx="9.5" cy="10" r="1.7" fill="#28A745"/>
<path d="M8.7 10L9.3 10.6L10.4 9.3" stroke="white" stroke-width="1" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M13 10H23" stroke="#D9E2EC" stroke-width="2" stroke-linecap="round"/>
<path d="M13 10H22" stroke="#0054A6" stroke-width="2" stroke-linecap="round"/>
<circle cx="9.5" cy="14" r="1.7" fill="#28A745"/>
<path d="M8.7 14L9.3 14.6L10.4 13.3" stroke="white" stroke-width="1" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M13 14H23" stroke="#D9E2EC" stroke-width="2" stroke-linecap="round"/>
<path d="M13 14H19.5" stroke="#F4B400" stroke-width="2" stroke-linecap="round"/>
<circle cx="9.5" cy="18" r="1.7" fill="white" stroke="#ED1C24" stroke-width="1.3"/>
<path d="M13 18H23" stroke="#D9E2EC" stroke-width="2" stroke-linecap="round"/>
<path d="M13 18H17" stroke="#ED1C24" stroke-width="2" stroke-linecap="round"/>
</svg> -->
</span>
</span>
<span class="app-brand-text demo menu-text fw-bold ms-3" style="text-transform: capitalize;">
AgroMonev
<img
src="{{ asset('assets/img/logo/agera_side.svg') }}"
alt="AGERA"
class="agera-logo"
>
</span>
</a>
@@ -46,73 +19,69 @@
<div class="menu-inner-shadow"></div>
<ul class="menu-inner py-1">
<!-- Dashboards -->
@php
$userRole = auth()->check() ? (auth()->user()->role instanceof \App\Enums\Role ? auth()->user()->role->value : auth()->user()->role) : null;
$user = auth()->user();
$userRole = $user?->role instanceof \App\Enums\Role ? $user->role->value : $user?->role;
$appName = env('APP_NAME');
$isAdmin = in_array($userRole, ['super_admin', 'admin'], true);
$isSuperAdmin = $userRole === 'super_admin';
$canViewDashboard = $isAdmin || $userRole === 'fasilitator';
@endphp
@if(in_array($userRole, ['super_admin', 'admin', 'fasilitator']) )
<li class="menu-item active open">
@if($canViewDashboard)
<li class="menu-item {{ request()->routeIs('dashboard.*') ? 'active open' : '' }}">
<a href="javascript:void(0);" class="menu-link menu-toggle">
<i class="menu-icon icon-base ti tabler-smart-home"></i>
<span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-dashboard"></i></span>
<div data-i18n="Dashboard">Dashboard</div>
</a>
@if(in_array($userRole, ['super_admin', 'admin']) && env('APP_NAME') == 'AgroMonev')
@if($isAdmin && $appName === 'AgroMonev')
<ul class="menu-sub">
<li class="menu-item {{ request()->routeIs(['dashboard.karakteristik-petani', 'dashboard.demografi-petani', 'dashboard.indeks-petani']) ? 'active' : '' }}">
<a href="{{ route('dashboard.karakteristik-petani') }}" class="menu-link">
<span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-user-circle"></i></span><div data-i18n="Karakteristik Petani">Karakteristik Petani</div>
</a>
</li>
</ul>
<ul class="menu-sub">
<li class="menu-item {{ request()->routeIs('dashboard.produktivitas') ? 'active' : '' }}">
<a href="{{ route('dashboard.produktivitas') }}" class="menu-link">
<div data-i18n="Produktivitas">Produktivitas</div>
</a>
</li>
</ul>
@endif
@if(in_array($userRole, ['super_admin', 'admin']) && env('APP_NAME') == 'AgroMonev')
<ul class="menu-sub">
<li class="menu-item {{ request()->routeIs('dashboard.indeks-petani') ? 'active' : '' }}">
<a href="{{ route('dashboard.indeks-petani') }}" class="menu-link">
<div data-i18n="Indeks Petani">Indeks Petani</div>
<span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-chart-bar"></i></span><div data-i18n="Produksi">Produksi</div>
</a>
</li>
</ul>
<ul class="menu-sub">
<li class="menu-item {{ request()->routeIs('dashboard.demografi-petani') ? 'active' : '' }}">
<a href="{{ route('dashboard.demografi-petani') }}" class="menu-link">
<div data-i18n="Demografi Petani">Demografi Petani</div>
<li class="menu-item {{ request()->routeIs('dashboard.pasar') ? 'active' : '' }}">
<a href="{{ route('dashboard.pasar') }}" class="menu-link">
<span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-building-store"></i></span><div data-i18n="Pasar">Pasar</div>
</a>
</li>
</ul>
<ul class="menu-sub">
<li class="menu-item {{ request()->routeIs('dashboard.indikator-pertanian') ? 'active' : '' }}">
<a href="{{ route('dashboard.indikator-pertanian') }}" class="menu-link">
<div data-i18n="Indikator Pertanian">Indikator Pertanian</div>
<span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-chart-line"></i></span><div data-i18n="Indikator Petani">Indikator Petani</div>
</a>
</li>
</ul>
@endif
@if(in_array($userRole, ['super_admin', 'admin', 'fasilitator']) && env('APP_NAME') == 'AgroMonev')
@if($canViewDashboard && $appName === 'AgroMonev')
<ul class="menu-sub">
<li class="menu-item {{ request()->routeIs('dashboard.survey-petani') ? 'active' : '' }}">
<a href="{{ route('dashboard.survey-petani') }}" class="menu-link">
<div data-i18n="Survey Petani">Survey Petani</div>
<span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-clipboard-list"></i></span><div data-i18n="Survei Petani">Survei Petani</div>
</a>
</li>
</ul>
<!-- <ul class="menu-sub">
<li class="menu-item {{ request()->routeIs('dashboard.petani') ? 'active' : '' }}">
<a href="{{ route('dashboard.petani') }}" class="menu-link">
<div data-i18n="Profil Petani">Profil Petani</div>
</a>
</li>
</ul> -->
@endif
@if(in_array($userRole, ['super_admin', 'admin']) && env('APP_NAME') == 'AgroMonev-xx')
@if($isAdmin && $appName === 'AgroMonev-xx')
<ul class="menu-sub">
<li class="menu-item {{ request()->routeIs('dashboard.bumdes') ? 'active' : '' }}">
<a href="{{ route('dashboard.bumdes') }}" class="menu-link">
<div data-i18n="Profil Pengurus BUMDes">Profil Pengurus BUMDes</div>
<span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-building-community"></i></span><div data-i18n="Profil Pengurus BUMDes">Profil Pengurus BUMDes</div>
</a>
</li>
</ul>
@@ -120,215 +89,201 @@
</li>
@endif
@if(in_array($userRole, ['fasilitator', 'super_admin', 'admin']) && env('APP_NAME') == 'AgroMonev')
<li class="menu-header small">
<span class="menu-header-text" data-i18n="Data Pertanian">Data Pertanian</span>
</li>
<li class="menu-item {{ request()->routeIs('petani.*') ? 'active' : '' }}">
<a href="{{ route('petani.index') }}" class="menu-link">
<i class="menu-icon icon-base ti tabler-file-description"></i>
<div data-i18n="Profil Petani">Profil Petani</div>
</a>
</li>
@endif
@if(in_array($userRole, ['super_admin', 'admin']) && env('APP_NAME') == 'AgroMonev')
<li class="menu-header small">
<span class="menu-header-text" data-i18n="Data BUMDes">Data BUMDes</span>
</li>
<li class="menu-item {{ request()->routeIs('bumdes.*') ? 'active' : '' }}">
<a href="{{ route('bumdes.index') }}" class="menu-link">
<i class="menu-icon icon-base ti tabler-file-description"></i>
<div data-i18n="Profil Pengurus BUMDes">Profil Pengurus BUMDes</div>
</a>
</li>
@endif
@if(in_array($userRole, ['super_admin', 'admin']) && env('APP_NAME') == 'AgroMonev')
<li class="menu-header small">
<span class="menu-header-text" data-i18n="Data Koperasi">Data Koperasi</span>
</li>
<li class="menu-item {{ request()->routeIs('koperasi.*') ? 'active' : '' }}">
<a href="{{ route('koperasi.pengurus.index') }}" class="menu-link">
<i class="menu-icon icon-base ti tabler-file-description"></i>
<div data-i18n="Profil Pengurus Koperasi">Profil Pengurus Koperasi</div>
</a>
</li>
@endif
@if(in_array($userRole, ['super_admin', 'admin']) && env('APP_NAME') == 'AgroPro')
<!-- Ekosistem -->
<li class="menu-header small">
<span class="menu-header-text" data-i18n="Ekosistem">Ekosistem</span>
</li>
<li class="menu-item {{ request()->routeIs(['profile.*', 'produksi.*', 'lahan.*', 'tanaman.*']) ? 'active open' : '' }}">
@if($canViewDashboard && $appName === 'AgroMonev')
<li class="menu-item {{ request()->routeIs(['petani.*', 'kelompok-tani.*', 'bumdes.*', 'koperasi.*']) ? 'active open' : '' }}">
<a href="javascript:void(0);" class="menu-link menu-toggle">
<i class="menu-icon icon-base ti tabler-file-description"></i>
<div data-i18n="Data Pertanian">Data Pertanian</div>
<span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-id-badge-2"></i></span>
<div data-i18n="Profil">Profil</div>
</a>
<ul class="menu-sub">
<li class="menu-item {{ request()->routeIs('petani.*') ? 'active' : '' }}">
<a href="{{ route('petani.index') }}" class="menu-link">
<span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-user-circle"></i></span>
<div data-i18n="Petani">Petani</div>
</a>
</li>
@if($isAdmin && $appName === 'AgroMonev')
<li class="menu-item {{ request()->routeIs('kelompok-tani.*') ? 'active' : '' }}">
<a href="{{ route('kelompok-tani.index') }}" class="menu-link">
<span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-users-group"></i></span>
<div data-i18n="Kelompok Tani">Kelompok Tani</div>
</a>
</li>
<li class="menu-item {{ request()->routeIs('bumdes.*') ? 'active' : '' }}">
<a href="{{ route('bumdes.index') }}" class="menu-link">
<span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-building-community"></i></span>
<div data-i18n="BUMDes">BUMDes</div>
</a>
</li>
<li class="menu-item {{ request()->routeIs('koperasi.*') ? 'active' : '' }}">
<a href="{{ route('koperasi.pengurus.index') }}" class="menu-link">
<span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-heart-handshake"></i></span>
<div data-i18n="Pengurus Koperasi">Pengurus Koperasi</div>
</a>
</li>
@endif
</ul>
</li>
@endif
@if($isAdmin && $appName === 'AgroPro')
<li class="menu-item {{ request()->routeIs(['petani.*', 'profile.*', 'produksi.*', 'lahan.*', 'tanaman.*', 'petani-cpm.*', 'lahan-cpcl.*', 'tabel.data.*', 'realisasi.*', 'kelompok-belajar-tani.*', 'workshop.*']) ? 'active open' : '' }}">
<a href="javascript:void(0);" class="menu-link menu-toggle">
<span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-layers-linked"></i></span>
<div data-i18n="Ekosistem">Ekosistem</div>
</a>
<ul class="menu-sub">
<li class="menu-item {{ request()->routeIs(['petani.*', 'profile.*', 'produksi.*', 'lahan.*', 'tanaman.*']) ? 'active open' : '' }}">
<a href="javascript:void(0);" class="menu-link menu-toggle">
<span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-id-badge-2"></i></span>
<div data-i18n="Profil">Profil</div>
</a>
<ul class="menu-sub">
<li class="menu-item {{ request()->routeIs('petani.*') ? 'active' : '' }}">
<a href="{{ route('petani.index') }}" class="menu-link"><div data-i18n="Profil Petani">Profil Petani</div></a>
<a href="{{ route('petani.index') }}" class="menu-link"><span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-user-circle"></i></span><div data-i18n="Petani">Petani</div></a>
</li>
<li class="menu-item {{ request()->routeIs('produksi.*') ? 'active' : '' }}">
<a href="{{ route('produksi.index') }}" class="menu-link"><div data-i18n="Hasil Panen">Hasil Panen</div></a>
<a href="{{ route('produksi.index') }}" class="menu-link"><span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-wheat"></i></span><div data-i18n="Hasil Panen">Hasil Panen</div></a>
</li>
<li class="menu-item {{ request()->routeIs('lahan.*') ? 'active' : '' }}">
<a href="{{ route('lahan.index') }}" class="menu-link"><div data-i18n="Pemantauan Lahan">Pemantauan Lahan</div></a>
<a href="{{ route('lahan.index') }}" class="menu-link"><span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-map"></i></span><div data-i18n="Pemantauan Lahan">Pemantauan Lahan</div></a>
</li>
<li class="menu-item {{ request()->routeIs('tanaman.*') ? 'active' : '' }}">
<a href="{{ route('tanaman.index') }}" class="menu-link"><div data-i18n="Pemantauan Tanaman">Pemantauan Tanaman</div></a>
<a href="{{ route('tanaman.index') }}" class="menu-link"><span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-plant"></i></span><div data-i18n="Pemantauan Tanaman">Pemantauan Tanaman</div></a>
</li>
</ul>
</li>
<li class="menu-item {{ request()->routeIs(['petani-cpm.*', 'lahan-cpcl.*', 'tabel.data.*']) ? 'active open' : '' }}">
<a href="javascript:void(0);" class="menu-link menu-toggle">
<i class="menu-icon icon-base ti tabler-folder-root"></i>
<span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-database"></i></span>
<div data-i18n="Basis Data Ekosistem">Basis Data Ekosistem</div>
</a>
<ul class="menu-sub">
<li class="menu-item {{ request()->routeIs('petani-cpm.*') ? 'active' : '' }}">
<a href="{{ route('petani-cpm.index') }}" class="menu-link"><div data-i18n="Petani CPM">Petani CPM</div></a>
<a href="{{ route('petani-cpm.index') }}" class="menu-link"><span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-users-group"></i></span><div data-i18n="Petani CPM">Petani CPM</div></a>
</li>
<li class="menu-item {{ request()->routeIs('lahan-cpcl.*') ? 'active' : '' }}">
<a href="{{ route('lahan-cpcl.index') }}" class="menu-link"><div data-i18n="Lahan CPCL">Lahan CPCL</div></a>
<a href="{{ route('lahan-cpcl.index') }}" class="menu-link"><span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-map-2"></i></span><div data-i18n="Lahan CPCL">Lahan CPCL</div></a>
</li>
</ul>
@foreach ($tabel_menu ?? [] as $tbl)
<ul class="menu-sub">
<li class="menu-item {{ request()->routeIs('tabel.data.*') && request()->route('tabel') == $tbl->id ? 'active' : '' }}">
<a href="{{ route('tabel.data.index', $tbl->id) }}" class="menu-link">
<div data-i18n="{{ $tbl->menu }}">{{ $tbl->menu }}</div>
<span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-table"></i></span><div data-i18n="{{ $tbl->menu }}">{{ $tbl->menu }}</div>
</a>
</li>
</ul>
@endforeach
</li>
<!-- <li class="menu-item">
<a href="javascript:void(0);" class="menu-link menu-toggle">
<i class="menu-icon icon-base ti tabler-line-scan"></i>
<div data-i18n="Tracebility">Tracebility</div>
</a>
<ul class="menu-sub">
<li class="menu-item">
<a href="#" class="menu-link">
<div data-i18n="Manajemen Batch & Kode">Manajemen Batch & Kode</div>
</a>
</li>
</ul>
<ul class="menu-sub">
<li class="menu-item">
<a href="#" class="menu-link">
<div data-i18n="Penelusuran Product">Penelusuran Product</div>
</a>
</li>
</ul>
</li> -->
<li class="menu-item {{ request()->routeIs(['realisasi.*', 'kelompok-belajar-tani.*', 'workshop.*']) ? 'active open' : '' }}">
<a href="javascript:void(0);" class="menu-link menu-toggle">
<i class="menu-icon icon-base ti tabler-webhook"></i>
<span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-users-group"></i></span>
<div data-i18n="Program & Komunitas">Program & Komunitas</div>
</a>
<ul class="menu-sub">
<li class="menu-item {{ request()->routeIs('realisasi.*') ? 'active' : '' }}">
<a href="{{ route('realisasi.index') }}" class="menu-link"><div data-i18n="Capaian Program">Capaian Program</div></a>
<a href="{{ route('realisasi.index') }}" class="menu-link"><span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-target"></i></span><div data-i18n="Capaian Program">Capaian Program</div></a>
</li>
<li class="menu-item {{ request()->routeIs('kelompok-belajar-tani.*') ? 'active' : '' }}">
<a href="{{ route('kelompok-belajar-tani.index') }}" class="menu-link"><div data-i18n="Kelompok Belajar Petani">Kelompok Belajar Petani</div></a>
<a href="{{ route('kelompok-belajar-tani.index') }}" class="menu-link"><span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-school"></i></span><div data-i18n="Kelompok Belajar Petani">Kelompok Belajar Petani</div></a>
</li>
<li class="menu-item {{ request()->routeIs('workshop.*') ? 'active' : '' }}">
<a href="{{ route('workshop.index') }}" class="menu-link"><div data-i18n="Workshop & Lokakarya">Workshop & Lokakarya</div></a>
<a href="{{ route('workshop.index') }}" class="menu-link"><span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-presentation"></i></span><div data-i18n="Workshop & Lokakarya">Workshop & Lokakarya</div></a>
</li>
</ul>
</li>
</ul>
</li>
@endif
@if(in_array($userRole, ['super_admin']))
<!-- Master Data -->
<li class="menu-header small">
<span class="menu-header-text" data-i18n="Master Data">Master Data</span>
</li>
<li class="menu-item">
@if($isSuperAdmin)
<li class="menu-item {{ request()->routeIs(['provinsi.*', 'kabupatenkota.*', 'kecamatan.*', 'desakelurahan.*', 'adat.*', 'keluarga.*', 'komoditas.*', 'jenis-komoditas.*', 'varietas.*']) ? 'active open' : '' }}">
<a href="javascript:void(0);" class="menu-link menu-toggle">
<i class="menu-icon icon-base ti tabler-world"></i>
<span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-database"></i></span>
<div data-i18n="Master Data">Master Data</div>
</a>
<ul class="menu-sub">
<li class="menu-item {{ request()->routeIs(['provinsi.*', 'kabupatenkota.*', 'kecamatan.*', 'desakelurahan.*']) ? 'active open' : '' }}">
<a href="javascript:void(0);" class="menu-link menu-toggle">
<span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-map-2"></i></span>
<div data-i18n="Wilayah">Wilayah</div>
</a>
<ul class="menu-sub">
<li class="menu-item"><a href="{{ route('provinsi.index') }}" class="menu-link"><div data-i18n="Provinsi">Provinsi</div></a></li>
<li class="menu-item"><a href="{{ route('kabupatenkota.index') }}" class="menu-link"><div data-i18n="Kabupaten Kota">Kabupaten Kota</div></a></li>
<li class="menu-item"><a href="{{ route('kecamatan.index') }}" class="menu-link"><div data-i18n="Kecamatan">Kecamatan</div></a></li>
<li class="menu-item"><a href="{{ route('desakelurahan.index') }}" class="menu-link"><div data-i18n="Desa Kelurahan">Desa Kelurahan</div></a></li>
<li class="menu-item"><a href="{{ route('provinsi.index') }}" class="menu-link"><span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-map"></i></span><div data-i18n="Provinsi">Provinsi</div></a></li>
<li class="menu-item"><a href="{{ route('kabupatenkota.index') }}" class="menu-link"><span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-building"></i></span><div data-i18n="Kabupaten Kota">Kabupaten Kota</div></a></li>
<li class="menu-item"><a href="{{ route('kecamatan.index') }}" class="menu-link"><span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-map-pin"></i></span><div data-i18n="Kecamatan">Kecamatan</div></a></li>
<li class="menu-item"><a href="{{ route('desakelurahan.index') }}" class="menu-link"><span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-home"></i></span><div data-i18n="Desa Kelurahan">Desa Kelurahan</div></a></li>
</ul>
</li>
<li class="menu-item">
<a href="{{ route('adat.index') }}" class="menu-link"><i class="menu-icon icon-base ti tabler-matrix"></i><div data-i18n="Adat">Adat</div></a>
<li class="menu-item {{ request()->routeIs('adat.*') ? 'active' : '' }}">
<a href="{{ route('adat.index') }}" class="menu-link"><span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-heart-handshake"></i></span><div data-i18n="Adat">Adat</div></a>
</li>
<li class="menu-item">
<a href="{{ route('keluarga.index') }}" class="menu-link"><i class="menu-icon icon-base ti tabler-sitemap"></i><div data-i18n="Keluarga">Keluarga</div></a>
<li class="menu-item {{ request()->routeIs('keluarga.*') ? 'active' : '' }}">
<a href="{{ route('keluarga.index') }}" class="menu-link"><span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-sitemap"></i></span><div data-i18n="Keluarga">Keluarga</div></a>
</li>
<li class="menu-item">
<li class="menu-item {{ request()->routeIs(['komoditas.*', 'jenis-komoditas.*', 'varietas.*']) ? 'active open' : '' }}">
<a href="javascript:void(0);" class="menu-link menu-toggle">
<i class="menu-icon icon-base ti tabler-plant"></i><div data-i18n="Tanaman">Tanaman</div>
<span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-plant"></i></span><div data-i18n="Tanaman">Tanaman</div>
</a>
<ul class="menu-sub">
<li class="menu-item"><a href="{{ route('komoditas.index') }}" class="menu-link"><div data-i18n="Komoditas">Komoditas</div></a></li>
<li class="menu-item"><a href="{{ route('jenis-komoditas.index') }}" class="menu-link"><div data-i18n="Jenis Komoditas">Jenis Komoditas</div></a></li>
<li class="menu-item"><a href="{{ route('varietas.index') }}" class="menu-link"><div data-i18n="Varietas">Varietas</div></a></li>
<li class="menu-item"><a href="{{ route('komoditas.index') }}" class="menu-link"><span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-plant"></i></span><div data-i18n="Komoditas">Komoditas</div></a></li>
<li class="menu-item"><a href="{{ route('jenis-komoditas.index') }}" class="menu-link"><span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-layout-grid"></i></span><div data-i18n="Jenis Komoditas">Jenis Komoditas</div></a></li>
<li class="menu-item"><a href="{{ route('varietas.index') }}" class="menu-link"><span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-leaf"></i></span><div data-i18n="Varietas">Varietas</div></a></li>
</ul>
</li>
</ul>
</li>
@endif
@if(in_array($userRole, ['super_admin', 'admin']))
<li class="menu-header small">
<span class="menu-header-text" data-i18n="Konfigurasi">Konfigurasi</span>
</li>
@if($isAdmin)
<li class="menu-item {{ request()->routeIs(['users.*', 'program.*', 'framework.*', 'group.*']) || (request()->routeIs('tabel.*') && !request()->routeIs('tabel.data.*')) ? 'active open' : '' }}">
<a href="javascript:void(0);" class="menu-link menu-toggle">
<span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-settings"></i></span>
<div data-i18n="Konfigurasi">Konfigurasi</div>
</a>
<ul class="menu-sub">
<li class="menu-item {{ request()->routeIs('users.*') ? 'active' : '' }}">
<a href="{{ route('users.index') }}" class="menu-link">
<i class="menu-icon icon-base ti tabler-users"></i><div data-i18n="Daftar Pengguna">Daftar Pengguna</div>
<span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-user-shield"></i></span><div data-i18n="Daftar Pengguna">Daftar Pengguna</div>
</a>
</li>
@endif
@if(in_array($userRole, ['super_admin', 'admin']) && env('APP_NAME') == 'AgroPro')
@if($isAdmin && $appName === 'AgroPro')
<li class="menu-item {{ request()->routeIs(['program.*', 'framework.*']) ? 'active open' : '' }}">
<a href="javascript:void(0);" class="menu-link menu-toggle">
<i class="menu-icon icon-base ti tabler-transform"></i><div data-i18n="Program Transformasi">Program Transformasi</div>
<span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-route"></i></span><div data-i18n="Program Transformasi">Program Transformasi</div>
</a>
<ul class="menu-sub">
<li class="menu-item {{ request()->routeIs('program.*') ? 'active' : '' }}">
<a href="{{ route('program.index') }}" class="menu-link"><div data-i18n="Program Transformasi">Program Transformasi</div></a>
<a href="{{ route('program.index') }}" class="menu-link"><span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-route"></i></span><div data-i18n="Program Transformasi">Program Transformasi</div></a>
</li>
<li class="menu-item {{ request()->routeIs('framework.*') ? 'active' : '' }}">
<a href="{{ route('framework.index') }}" class="menu-link"><div data-i18n="Framework">Framework</div></a>
<a href="{{ route('framework.index') }}" class="menu-link"><span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-sitemap"></i></span><div data-i18n="Framework">Framework</div></a>
</li>
</ul>
</li>
<li class="menu-item {{ request()->routeIs(['group.*', 'tabel.*']) && !request()->routeIs('tabel.data.*') ? 'active open' : '' }}">
<a href="javascript:void(0);" class="menu-link menu-toggle">
<i class="menu-icon icon-base ti tabler-database"></i><div data-i18n="Basis Data Ekosistem">Basis Data Ekosistem</div>
<span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-database"></i></span><div data-i18n="Basis Data Ekosistem">Basis Data Ekosistem</div>
</a>
<ul class="menu-sub">
<li class="menu-item {{ request()->routeIs('group.*') ? 'active' : '' }}">
<a href="{{ route('group.index') }}" class="menu-link"><div data-i18n="Kelompok">Kelompok</div></a>
<a href="{{ route('group.index') }}" class="menu-link"><span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-users-group"></i></span><div data-i18n="Kelompok">Kelompok</div></a>
</li>
<li class="menu-item {{ request()->routeIs('tabel.*') && !request()->routeIs('tabel.data.*') ? 'active' : '' }}">
<a href="{{ route('tabel.index') }}" class="menu-link"><div data-i18n="Tabel">Tabel</div></a>
<a href="{{ route('tabel.index') }}" class="menu-link"><span class="menu-icon sidebar-icon" aria-hidden="true"><i class="icon-base ti tabler-table"></i></span><div data-i18n="Tabel">Tabel</div></a>
</li>
</ul>
</li>
@endif
<!-- Misc -->
<!-- <li class="menu-header small">
<span class="menu-header-text" data-i18n="Misc">Misc</span>
</ul>
</li>
<li class="menu-item">
<a href="https://docs.google.com/spreadsheets/d/1nbh4xW-K_GwRW34-cD81p9B-LnLvkvro/edit?usp=sharing&ouid=103442063105125817060&rtpof=true&sd=true" target="_blank" class="menu-link">
<i class="menu-icon icon-base ti tabler-file-description"></i><div data-i18n="Documentation">Documentation</div>
</a>
</li> -->
@endif
</ul>
</aside>
@@ -234,7 +234,7 @@
</div>
<div class="mt-4 text-end">
<a href="{{ route('petani.index') }}" class="btn btn-secondary me-2 wave-effect wave-light">Kembali</a>
<a href="{{ route('petani.index') }}#data-petani" class="btn btn-secondary me-2 wave-effect wave-light">Kembali</a>
<button type="submit" class="btn btn-success data-submit wave-effect wave-light"><i class="ti tabler-device-floppy me-2"></i> Simpan Profil</button>
</div>
@@ -30,11 +30,110 @@
<div class="card mb-6">
<div class="card-body pt-12">
<h5 class="pb-3 border-bottom mb-4">Pengalaman Saluran Lain</h5>
<h5 class="pb-4 border-bottom mb-4">Saluran Penjualan & Akses Pasar</h5>
<h5 class="mb-4 mt-4">Saluran Penjualan Utama</h5>
<div class="mb-4">
<p class="form-label d-block">Kepada siapa Bapak/Ibu biasanya menjual hasil panen? (boleh pilih lebih dari satu)</p>
<div class="row">
@php
$tujuanOptions = ['Tengkulak/pedagang lokal', 'Koperasi', 'BUMDes', 'Langsung ke pembeli/eksportir'];
@endphp
@foreach($tujuanOptions as $tujuan)
<div class="col-md-6">
<div class="form-check mb-2">
<input class="form-check-input" type="checkbox" name="tujuan_penjualan[]" value="{{ $tujuan }}" id="tujuan-{{ Str::slug($tujuan) }}" {{ in_array($tujuan, (array) old('tujuan_penjualan', $aksesPasar?->tujuan_penjualan ?? []), true) ? 'checked' : '' }}>
<label class="form-check-label" for="tujuan-{{ Str::slug($tujuan) }}">{{ $tujuan }}</label>
</div>
</div>
@endforeach
<div class="col-12 mt-2">
<div class="d-flex align-items-center gap-2 flex-wrap">
<div class="form-check mb-0">
<input class="form-check-input" type="checkbox" name="tujuan_penjualan[]" value="Lainnya" id="tujuan-lainnya" {{ in_array('Lainnya', (array) old('tujuan_penjualan', $aksesPasar?->tujuan_penjualan ?? []), true) ? 'checked' : '' }}>
<label class="form-check-label" for="tujuan-lainnya">Lainnya:</label>
</div>
<input type="text" class="form-control w-auto flex-grow-1 max-w-400" style="max-width: 400px;" name="tujuan_penjualan_lainnya" value="{{ old('tujuan_penjualan_lainnya', $aksesPasar?->tujuan_penjualan_lainnya) }}" placeholder="Sebutkan lainnya...">
</div>
</div>
</div>
</div>
<h5 class="mb-4 mt-4">Sifat Hubungan Dengan Pembeli</h5>
<div class="mb-4">
<p class="form-label d-block">Bagaimana sifat hubungan jual Bapak/Ibu dengan pembeli utama saat ini?</p>
<div class="row">
@php
$sifatHubunganOptions = [
'Bebas, bisa pindah pembeli kapan saja',
'Langganan tetap, tapi tidak terikat',
'Berbasis hutang, harus jual ke sana karena ada pinjaman',
'Titip jual,dibayar setelah produk terjual',
'Ada komitmen volume, sudah ada kesepakatan jumlah',
'Semi-kontrak, ada kesepakatan harga tertentu'
];
@endphp
@foreach($sifatHubunganOptions as $sifat)
<div class="col-md-6">
<div class="form-check mb-2">
<input class="form-check-input" type="radio" name="sifat_hubungan_bisnis" value="{{ $sifat }}" id="sifat-{{ Str::slug($sifat) }}" {{ old('sifat_hubungan_bisnis', $aksesPasar?->sifat_hubungan_bisnis) === $sifat ? 'checked' : '' }}>
<label class="form-check-label" for="sifat-{{ Str::slug($sifat) }}">{{ $sifat }}</label>
</div>
</div>
@endforeach
</div>
</div>
<h5 class="mb-4 mt-4">Bentuk Produk Yang Dijual</h5>
<div class="mb-4">
<p class="form-label d-block">Dalam bentuk apa produk dijual?</p>
<div class="row">
@php
$bentukPenjualanOptions = [
'Buah segar hasil petik (gelondongan)',
'Biji/polong basah (belum dikeringkan)',
'Biji/polong kering/biji kupas',
'Terfermentasi'
];
@endphp
@foreach($bentukPenjualanOptions as $bentuk)
<div class="col-md-6">
<div class="form-check mb-2">
<input class="form-check-input" type="radio" name="bentuk_penjualan" value="{{ $bentuk }}" id="bentuk-{{ Str::slug($bentuk) }}" {{ old('bentuk_penjualan', $aksesPasar?->bentuk_penjualan) === $bentuk ? 'checked' : '' }}>
<label class="form-check-label" for="bentuk-{{ Str::slug($bentuk) }}">{{ $bentuk }}</label>
</div>
</div>
@endforeach
</div>
</div>
<h5 class="mb-4 mt-4">Frekuensi Penjualan</h5>
<div class="mb-4">
<p class="form-label d-block">Kapan biasanya menjual hasil panen?</p>
<div class="d-flex flex-column">
@php
$frekuensiOptions = [
'Langsung setelah panen',
'Ditumpuk dulu beberapa waktu',
'Saat butuh uang mendesak'
];
@endphp
@foreach($frekuensiOptions as $freq)
<div class="form-check mb-2">
<input class="form-check-input" type="radio" name="frekuensi_penjualan" value="{{ $freq }}" id="freq-{{ Str::slug($freq) }}" {{ old('frekuensi_penjualan', $aksesPasar?->frekuensi_penjualan) === $freq ? 'checked' : '' }}>
<label class="form-check-label" for="freq-{{ Str::slug($freq) }}">{{ $freq }}</label>
</div>
@endforeach
</div>
</div>
<h5 class="pb-4 border-bottom mb-4 mt-4">Pengalaman Saluran Lain</h5>
<div class="row">
<div class="col-md-6 mb-4 form-control-validation">
<label class="form-label d-block fw-bold">Pengalaman & akses ke saluran lain</label>
<div class="col-md-12 mb-4 form-control-validation">
<label class="form-label d-block">Apakah pernah menjual ke tempat lain sebelumnya?</label>
@foreach($skorPengalamanOptions as $option)
<div class="form-check mb-2">
<input class="form-check-input @error('skor_pengalaman') is-invalid @enderror" type="radio"
@@ -46,41 +145,67 @@
@error('skor_pengalaman')<div class="invalid-feedback d-block">{{ $message }}</div>@enderror
</div>
<div class="col-md-4 mb-4 form-control-validation">
<label class="form-label" for="alasan-berhenti">Alasan berhenti</label>
<input type="text" class="form-control @error('alasan_berhenti') is-invalid @enderror" id="alasan-berhenti" name="alasan_berhenti" value="{{ old('alasan_berhenti', $aksesPasar?->alasan_berhenti) }}" placeholder="Opsional (jika sudah berhenti)...">
@error('alasan_berhenti')<div class="invalid-feedback">{{ $message }}</div>@enderror
</div>
<div class="col-md-12" id="blok-saluran-lanjutan">
<div class="row">
<div class="col-md-6 mb-4 form-control-validation">
<label class="form-label d-block mb-2" for="saluran-terdahulu">Dulu pernah jual ke?</label>
@foreach($saluranTerdahuluOptions as $option)
<div class="form-check mb-2">
<input class="form-check-input @error('saluran_terdahulu') is-invalid @enderror"
type="checkbox"
id="saluran-terdahulu-{{ Str::slug($option['value']) }}"
name="saluran_terdahulu[]"
value="{{ $option['value'] }}"
{{ in_array($option['value'], (array) old('saluran_terdahulu', $aksesPasar?->saluran_terdahulu ?? []), true) ? 'checked' : '' }}>
<label class="form-check-label" for="saluran-terdahulu-{{ Str::slug($option['value']) }}">
{{ $option['label'] }}
</label>
<div class="col-md-6 mb-4 form-control-validation">
<label class="form-label" for="alasan-berhenti">Alasan berhenti</label>
<input type="text" class="form-control @error('alasan_berhenti') is-invalid @enderror" id="alasan-berhenti" name="alasan_berhenti" value="{{ old('alasan_berhenti', $aksesPasar?->alasan_berhenti) }}" placeholder="Opsional (jika sudah berhenti)...">
@error('alasan_berhenti')<div class="invalid-feedback">{{ $message }}</div>@enderror
</div>
@endforeach
@error('saluran_terdahulu')<div class="invalid-feedback d-block">{{ $message }}</div>@enderror
</div>
<div class="col-md-4 mb-4 form-control-validation">
<label class="form-label" for="saluran-lainnya">Lainnya</label>
<input type="text" class="form-control @error('saluran_terdahulu_lainnya') is-invalid @enderror" id="saluran-lainnya" name="saluran_terdahulu_lainnya" value="{{ old('saluran_terdahulu_lainnya', $aksesPasar?->saluran_terdahulu_lainnya) }}" placeholder="Sebutkan saluran lainnya...">
@error('saluran_terdahulu_lainnya')<div class="invalid-feedback">{{ $message }}</div>@enderror
<div class="col-md-6 mb-4"></div>
<div class="col-md-12 mb-4 form-control-validation">
<label class="form-label d-block" for="saluran-terdahulu">Jika pernah, dulu menjual ke mana? (boleh pilih lebih dari satu)</label>
<div class="row">
@foreach($saluranTerdahuluOptions as $option)
@if($option['value'] !== $saluranLainnyaValue)
<div class="col-md-6">
<div class="form-check mb-2">
<input class="form-check-input @error('saluran_terdahulu') is-invalid @enderror"
type="checkbox"
id="saluran-terdahulu-{{ Str::slug($option['value']) }}"
name="saluran_terdahulu[]"
value="{{ $option['value'] }}"
{{ in_array($option['value'], (array) old('saluran_terdahulu', $aksesPasar?->saluran_terdahulu ?? []), true) ? 'checked' : '' }}>
<label class="form-check-label" for="saluran-terdahulu-{{ Str::slug($option['value']) }}">
{{ $option['label'] }}
</label>
</div>
</div>
@endif
@endforeach
<div class="col-12 mt-2">
<div class="d-flex align-items-center gap-2 flex-wrap">
<div class="form-check mb-0">
<input class="form-check-input @error('saluran_terdahulu') is-invalid @enderror"
type="checkbox"
id="saluran-terdahulu-lainnya-checkbox"
name="saluran_terdahulu[]"
value="{{ $saluranLainnyaValue }}"
{{ in_array($saluranLainnyaValue, (array) old('saluran_terdahulu', $aksesPasar?->saluran_terdahulu ?? []), true) ? 'checked' : '' }}>
<label class="form-check-label text-nowrap" for="saluran-terdahulu-lainnya-checkbox">
Lainnya:
</label>
</div>
<input type="text" class="form-control w-auto flex-grow-1 @error('saluran_terdahulu_lainnya') is-invalid @enderror" style="max-width: 400px;" id="saluran-lainnya" name="saluran_terdahulu_lainnya" value="{{ old('saluran_terdahulu_lainnya', $aksesPasar?->saluran_terdahulu_lainnya) }}" placeholder="Sebutkan saluran lainnya...">
</div>
</div>
</div>
@error('saluran_terdahulu')<div class="invalid-feedback d-block">{{ $message }}</div>@enderror
@error('saluran_terdahulu_lainnya')<div class="invalid-feedback d-block">{{ $message }}</div>@enderror
</div>
</div>
</div>
</div>
<h5 class="pb-3 border-bottom mb-4 mt-4">HARGA DI SETIAP TITIK</h5>
<h6 class="mb-2">Harga Jual Panen Terakhir</h6>
<p class="mb-1">Harga jual komoditas tiga musim panen terakhir:</p>
<p class="mb-4">Dalam tiga musim panen terakhir, berapa harga jual yang Bapak/Ibu terima untuk komoditas ini sesuai dengan bentuk produk yang dijual?</p>
<h5 class="pb-4 border-bottom mb-4 mt-4">Harga Di Setiap Titik</h5>
<h5 class="mb-4 mt-4">Harga Jual Panen Terakhir</h5>
<p class="form-label d-block">Harga jual komoditas tiga musim panen terakhir:</p>
<p class="form-label d-block">Dalam tiga musim panen terakhir, berapa harga jual yang Bapak/Ibu terima untuk komoditas ini sesuai dengan bentuk produk yang dijual?</p>
<div class="table-responsive mb-4">
<table class="table table-bordered align-middle">
@@ -97,7 +222,7 @@
<tr>
<td>
<input type="hidden" name="harga_jual[{{ $row['index'] }}][bentuk_produk]" value="{{ $row['bentuk'] }}">
<strong>{{ $row['bentuk'] }}:</strong>
{{ $row['bentuk'] }}:
</td>
<td>
<div class="input-group">
@@ -126,18 +251,87 @@
</table>
</div>
<h5 class="pb-3 border-bottom mb-4 mt-4">PELUANG NILAI TAMBAH</h5>
<h5 class="mb-4 mt-4">Sumber Informasi Harga</h5>
<div class="mb-4">
<div class="border rounded p-4 mb-4">
<div class="mb-4">
<h6 class="mb-1 text-primary">Detail Praktik Fermentasi</h6>
<p class="text-muted small mb-0">Apakah sudah ada praktik fermentasi atau pengolahan pascapanen yang Ibu/Bapak lakukan?</p>
<p class="form-label d-block">Sebelum menjual, apakah Bapak/Ibu sudah tahu harga yang akan diterima?</p>
<div class="row">
@php
$tahuHargaOptions = [
'Selalu tahu sebelum pergi jual',
'Kadang tahu, kadang tidak',
'Baru tahu saat sudah di tempat pembeli',
'Tidak pernah tahu sebelumnya — langsung jual saja'
];
@endphp
@foreach($tahuHargaOptions as $tahu)
<div class="col-md-6">
<div class="form-check mb-2">
<input class="form-check-input @error('tahu_harga_sebelumnya') is-invalid @enderror" type="radio" name="tahu_harga_sebelumnya" value="{{ $tahu }}" id="tahu-{{ Str::slug($tahu) }}" {{ old('tahu_harga_sebelumnya', $aksesPasar?->tahu_harga_sebelumnya) === $tahu ? 'checked' : '' }}>
<label class="form-check-label" for="tahu-{{ Str::slug($tahu) }}">{{ $tahu }}</label>
</div>
</div>
@endforeach
</div>
@error('tahu_harga_sebelumnya')<div class="invalid-feedback d-block">{{ $message }}</div>@enderror
</div>
<div class="mb-0">
<p class="form-label d-block">Dari mana biasanya Bapak/Ibu tahu harga hasil panen sebelum menjual? (boleh pilih lebih dari satu)</p>
<div class="row">
@php
$sumberInfoHargaOptions = [
'Tengkulak/pedagang yang datang langsung',
'Sesama petani/tetangga',
'PPL/petugas dinas',
'HP/WhatsApp/internet',
'Radio/TV',
'Facebook',
'Tiktok',
'Instagram',
'Tidak pernah cek — langsung jual saja'
];
$sumberInfoLainnyaValue = 'Lainnya';
@endphp
@foreach($sumberInfoHargaOptions as $sumber)
<div class="col-md-4">
<div class="form-check mb-2">
<input class="form-check-input @error('sumber_info_harga') is-invalid @enderror" type="checkbox" name="sumber_info_harga[]" value="{{ $sumber }}" id="sumber-{{ Str::slug($sumber) }}" {{ in_array($sumber, (array) old('sumber_info_harga', $aksesPasar?->sumber_info_harga ?? []), true) ? 'checked' : '' }}>
<label class="form-check-label" for="sumber-{{ Str::slug($sumber) }}">{{ $sumber }}</label>
</div>
</div>
@endforeach
<div class="col-12 mt-2">
<div class="d-flex align-items-center gap-2 flex-wrap">
<div class="form-check mb-0">
<input class="form-check-input @error('sumber_info_harga') is-invalid @enderror" type="checkbox" name="sumber_info_harga[]" value="{{ $sumberInfoLainnyaValue }}" id="sumber-lainnya-checkbox" {{ in_array($sumberInfoLainnyaValue, (array) old('sumber_info_harga', $aksesPasar?->sumber_info_harga ?? []), true) ? 'checked' : '' }}>
<label class="form-check-label text-nowrap" for="sumber-lainnya-checkbox">Lainnya:</label>
</div>
<input type="text" class="form-control w-auto flex-grow-1 @error('sumber_info_harga_lainnya') is-invalid @enderror" style="max-width: 400px;" id="sumber-info-lainnya-input" name="sumber_info_harga_lainnya" value="{{ old('sumber_info_harga_lainnya', $aksesPasar?->sumber_info_harga_lainnya) }}" placeholder="Sebutkan sumber lainnya...">
</div>
</div>
</div>
@error('sumber_info_harga')<div class="invalid-feedback d-block">{{ $message }}</div>@enderror
@error('sumber_info_harga_lainnya')<div class="invalid-feedback d-block">{{ $message }}</div>@enderror
</div>
</div>
<h5 class="pb-4 border-bottom mb-4 mt-4">Peluang Nilai Tambah</h5>
<div class="mb-4">
<div class="mb-4">
<h5 class="mb-4 mt-4">Detail Praktik Fermentasi</h5>
<p class="form-label d-block">Apakah sudah ada praktik fermentasi atau pengolahan pascapanen yang Ibu/Bapak lakukan?</p>
</div>
<div class="row">
<div class="col-md-4 mb-4 form-control-validation d-flex flex-column">
<div class="mb-2 d-flex align-items-end">
<label class="form-label mb-0" for="praktik-fermentasi">Praktik fermentasi yang dilakukan</label>
<div class="d-flex align-items-end">
<label class="form-label" for="praktik-fermentasi">Praktik fermentasi yang dilakukan</label>
</div>
<div>
@foreach($praktikFermentasiOptions as $option)
@@ -158,8 +352,8 @@
</div>
<div class="col-md-4 mb-4 form-control-validation d-flex flex-column">
<div class="mb-2 d-flex align-items-end">
<label class="form-label mb-0" for="proses-fermentasi">Jika ya, bagaimana prosesnya?</label>
<div class="d-flex align-items-end">
<label class="form-label" for="proses-fermentasi">Jika ya, bagaimana prosesnya?</label>
</div>
<div>
<textarea class="form-control @error('proses_fermentasi') is-invalid @enderror" id="proses-fermentasi" name="proses_fermentasi" rows="2" placeholder="Ceritakan prosesnya di sini...">{{ old('proses_fermentasi', $aksesPasar?->proses_fermentasi) }}</textarea>
@@ -168,8 +362,8 @@
</div>
<div class="col-md-4 mb-4 form-control-validation d-flex flex-column">
<div class="mb-2 d-flex align-items-end">
<label class="form-label mb-0" for="model-fermentasi">Menurut Bapak/Ibu, apa model yang paling memungkinkan?</label>
<div class="d-flex align-items-end">
<label class="form-label" for="model-fermentasi">Menurut Bapak/Ibu, apa model yang paling memungkinkan?</label>
</div>
<div>
<textarea class="form-control @error('model_fermentasi_memungkinkan') is-invalid @enderror" id="model-fermentasi" name="model_fermentasi_memungkinkan" rows="2" placeholder="Model fermentasinya...">{{ old('model_fermentasi_memungkinkan', $aksesPasar?->model_fermentasi_memungkinkan) }}</textarea>
@@ -179,11 +373,11 @@
</div>
</div>
<h5 class="pb-3 border-bottom mb-4 mt-4">AKSES PASAR PREMIUM</h5>
<h6 class="mb-3">Kesadaran Pasar Premium*</h6>
<h5 class="pb-4 border-bottom mb-4 mt-4">Akses Pasar Premium</h5>
<h5 class="mb-4 mt-4">Kesadaran Pasar Premium*</h5>
<div class="row">
<div class="col-md-6 mb-4 form-control-validation">
<label class="form-label d-block mb-2" for="skor-premium">Pengetahuan & akses pasar premium</label>
<label class="form-label d-block" for="skor-premium">Pengetahuan & akses pasar premium</label>
@foreach($skorKesadaranPremiumOptions as $option)
<div class="form-check mb-2">
<input class="form-check-input @error('skor_kesadaran_premium') is-invalid @enderror"
@@ -201,16 +395,16 @@
</div>
<div class="col-md-6 mb-4 form-control-validation">
<label class="form-label mb-2" for="sumber-info">Sumber informasi utama</label>
<label class="form-label" for="sumber-info">Sumber informasi utama</label>
<input type="text" class="form-control @error('sumber_info_premium') is-invalid @enderror" id="sumber-info" name="sumber_info_premium" value="{{ old('sumber_info_premium', $aksesPasar?->sumber_info_premium) }}">
@error('sumber_info_premium')<div class="invalid-feedback">{{ $message }}</div>@enderror
</div>
</div>
<h5 class="pb-3 border-bottom mb-4 mt-4">KEPERCAYAAN & PERSEPSI RISIKO</h5>
<h5 class="pb-4 border-bottom mb-4 mt-4">Kepercayaan & Persepsi Risiko</h5>
<div class="row">
<div class="col-md-6 mb-4 form-control-validation">
<label class="form-label d-block mb-2" for="tingkat-kepercayaan">Seberapa percaya Bapak/Ibu untuk menjual ke pembeli atau pasar baru, di luar pembeli yang biasa?</label>
<label class="form-label d-block" for="tingkat-kepercayaan">Seberapa percaya Bapak/Ibu untuk menjual ke pembeli atau pasar baru, di luar pembeli yang biasa?</label>
@foreach($tingkatKepercayaanOptions as $option)
<div class="form-check mb-2">
<input class="form-check-input @error('tingkat_kepercayaan_pembeli_baru') is-invalid @enderror"
@@ -228,7 +422,7 @@
</div>
<div class="col-md-6 mb-4 form-control-validation">
<label class="form-label mb-2" for="alasan-kepercayaan">Apa yang membuat Bapak/Ibu ragu atau yakin untuk mencoba pembeli baru?</label>
<label class="form-label" for="alasan-kepercayaan">Apa yang membuat Bapak/Ibu ragu atau yakin untuk mencoba pembeli baru?</label>
<textarea class="form-control @error('alasan_kepercayaan_pembeli_baru') is-invalid @enderror"
id="alasan-kepercayaan"
name="alasan_kepercayaan_pembeli_baru"
@@ -239,8 +433,9 @@
</div>
<div class="mt-4 text-end">
<a href="{{ route('petani.index') }}#data-petani" class="btn btn-secondary me-2">Kembali</a>
<button type="submit" class="btn btn-success">
<i class="icon-base ti tabler-device-floppy me-1_5 icon-sm"></i>Simpan Akses Pasar
<i class="icon-base ti tabler-check me-1_5 icon-sm"></i>Simpan
</button>
</div>
</div>
@@ -258,6 +453,8 @@
const saluranLainnyaValue = @json($saluranLainnyaValue);
const praktikBelumPernahValue = String(@json($praktikBelumPernahValue));
const skorPengalamanBerhentiValues = @json($skorPengalamanBerhentiValues ?? [2,3]).map(Number);
const skorBelumPernahValue = "1";
const sumberInfoLainnyaValue = @json($sumberInfoLainnyaValue ?? 'Lainnya');
function onlyDigits(value) {
return String(value || '').replace(/\D/g, '');
@@ -265,10 +462,25 @@
function formatRupiah(value) {
const digits = onlyDigits(value);
return digits.replace(/\B(?=(\d{3})+(?!\d))/g, '.');
}
function toggleSaluranLanjutan() {
const selectedRadio = $('input[name="skor_pengalaman"]:checked');
const val = selectedRadio.val();
const labelText = selectedRadio.parent().text().toLowerCase();
const isBelumPernah = !val || labelText.includes('belum pernah') || val === skorBelumPernahValue;
if (isBelumPernah) {
$('#blok-saluran-lanjutan').addClass('d-none');
$('#blok-saluran-lanjutan input[type="text"]').val('');
$('#blok-saluran-lanjutan input[type="checkbox"]').prop('checked', false);
} else {
$('#blok-saluran-lanjutan').removeClass('d-none');
}
}
function syncAlasanBerhenti() {
const val = Number($('input[name="skor_pengalaman"]:checked').val());
const isBerhenti = skorPengalamanBerhentiValues.includes(val);
@@ -282,6 +494,12 @@
if (!isLainnya) $('#saluran-lainnya').val('');
}
function syncSumberInfoLainnya() {
const isLainnya = $('input[name="sumber_info_harga[]"][value="' + sumberInfoLainnyaValue + '"]').is(':checked');
$('#sumber-info-lainnya-input').prop('disabled', !isLainnya);
if (!isLainnya) $('#sumber-info-lainnya-input').val('');
}
function syncPraktikFermentasi() {
const isBelumPernah = $('input[name="praktik_fermentasi"]:checked').val() === praktikBelumPernahValue;
$('#proses-fermentasi, #model-fermentasi').prop('disabled', isBelumPernah);
@@ -290,9 +508,15 @@
}
}
$('input[name="skor_pengalaman"]').on('change', syncAlasanBerhenti);
$('input[name="skor_pengalaman"]').on('change', function () {
toggleSaluranLanjutan();
syncAlasanBerhenti();
});
$('input[name="saluran_terdahulu[]"]').on('change', syncSaluranLainnya);
$('input[name="sumber_info_harga[]"]').on('change', syncSumberInfoLainnya);
$('input[name="praktik_fermentasi"]').on('change', syncPraktikFermentasi);
$(document).on('input', '.rupiah-input', function () {
const cursorAtEnd = this.selectionStart === this.value.length;
this.value = formatRupiah(this.value);
@@ -301,12 +525,15 @@
this.setSelectionRange(this.value.length, this.value.length);
}
});
$('.rupiah-input').each(function () {
this.value = formatRupiah(this.value);
});
toggleSaluranLanjutan();
syncAlasanBerhenti();
syncSaluranLainnya();
syncSumberInfoLainnya();
syncPraktikFermentasi();
});
</script>
@@ -13,6 +13,7 @@
@push('scripts')
<script src="https://cdnjs.cloudflare.com/ajax/libs/croppie/2.6.5/croppie.min.js"></script>
@endpush
@section('content')
<div class="container-xxl flex-grow-1 container-p-y">
@@ -43,13 +44,18 @@
<div class="col-md-3">
<div class="user-avatar-section">
<div class=" d-flex align-items-center flex-column">
<img id="fotoPreview"
class="mb-3 shadow-sm rounded-3"
<button type="button" class="border-0 bg-transparent p-0 mb-3 rounded-3"
data-bs-toggle="modal" data-bs-target="#fotoProfilModal"
aria-label="Lihat foto profil petani" title="Lihat foto profil petani">
<img id="fotoPreview"
class="shadow-sm rounded-3"
src="{{ $petani->foto ? route('petani.show-image', ['id' => $petani->id, 'field' => 'foto']) : asset('assets/img/avatars/3.jpg') }}"
alt="Foto profil petani"
data-bs-toggle="tooltip" data-bs-placement="top"
data-bs-original-title="Klik untuk mengganti foto profil petani"
style="width: 120px !important; height: 120px !important; cursor:pointer; object-fit:cover; display:block; margin: 0 auto;" />
</button>
<button type="button" id="btnGantiFoto" class="btn btn-sm btn-outline-primary mb-3">
Ganti Foto
</button>
<button type="button" id="btnHapusFoto" class="btn btn-sm btn-outline-danger mb-4" style="display: {{ $petani->foto ? 'block' : 'none' }};">
Hapus Foto
@@ -113,7 +119,7 @@
<select class="form-control @error('pendidikan_terakhir') is-invalid @enderror" id="edit-petani-pendidikan-terakhir" name="pendidikan_terakhir">
<option value="">Pilih Pendidikan Terakhir</option>
@foreach($pendidikan as $value)
<option value="{{ $value['value'] }}" {{ old('pendidikan_terakhir', $petani->pendidikan_terakhir->value) == $value['value'] ? 'selected' : '' }}>{{ $value['label'] }}</option>
<option value="{{ $value['value'] }}" {{ old('pendidikan_terakhir', $petani->pendidikan_terakhir?->value) == $value['value'] ? 'selected' : '' }}>{{ $value['label'] }}</option>
@endforeach
</select>
@error('pendidikan_terakhir')<div class="error invalid-feedback">{{ $message }}</div>@enderror
@@ -123,7 +129,7 @@
<select class="form-control @error('status_perkawinan') is-invalid @enderror" id="edit-petani-status-pernikahan" name="status_perkawinan">
<option value="">Pilih Status Pernikahan</option>
@foreach($pernikahan as $value)
<option value="{{ $value['value'] }}" {{ old('status_perkawinan', $petani->status_perkawinan->value) == $value['value'] ? 'selected' : '' }}>{{ $value['label'] }}</option>
<option value="{{ $value['value'] }}" {{ old('status_perkawinan', $petani->status_perkawinan?->value) == $value['value'] ? 'selected' : '' }}>{{ $value['label'] }}</option>
@endforeach
</select>
@error('status_perkawinan')<div class="error invalid-feedback">{{ $message }}</div>@enderror
@@ -159,7 +165,7 @@
<select class="form-control @error('provinsi_id') is-invalid @enderror" id="edit-petani-provinsi" name="provinsi_id">
<option value="">Pilih Provinsi</option>
@foreach($provinsi as $p)
<option value="{{ $p->id }}" {{ old('provinsi_id', $petani->desaKelurahan->kecamatan->kabupatenKota->provinsi_id) == $p->id ? 'selected' : '' }}>{{ $p->nama }}</option>
<option value="{{ $p->id }}" {{ old('provinsi_id', $petani->desaKelurahan?->kecamatan?->kabupatenKota?->provinsi_id) == $p->id ? 'selected' : '' }}>{{ $p->nama }}</option>
@endforeach
</select>
@error('provinsi_id')<div class="error invalid-feedback">{{ $message }}</div>@enderror
@@ -168,7 +174,9 @@
<label class="form-label" for="edit-petani-kabupaten">Kabupaten/Kota<span class="text-danger">*</span></label>
<select class="form-control @error('kabupaten_id') is-invalid @enderror" id="edit-petani-kabupaten" name="kabupaten_id" >
<option value="">Pilih Kabupaten/Kota</option>
@if($petani->desaKelurahan?->kecamatan?->kabupatenKota)
<option value="{{ $petani->desaKelurahan->kecamatan->kabupatenKota->id }}" selected>{{ $petani->desaKelurahan->kecamatan->kabupatenKota->nama }}</option>
@endif
</select>
@error('kabupaten_id')<div class="error invalid-feedback">{{ $message }}</div>@enderror
</div>
@@ -176,7 +184,9 @@
<label class="form-label" for="edit-petani-kecamatan">Kecamatan<span class="text-danger">*</span></label>
<select class="form-control @error('kecamatan_id') is-invalid @enderror" id="edit-petani-kecamatan" name="kecamatan_id" >
<option value="">Pilih Kecamatan</option>
@if($petani->desaKelurahan?->kecamatan)
<option value="{{ $petani->desaKelurahan->kecamatan->id }}" selected>{{ $petani->desaKelurahan->kecamatan->nama }}</option>
@endif
</select>
@error('kecamatan_id')<div class="error invalid-feedback">{{ $message }}</div>@enderror
</div>
@@ -184,7 +194,9 @@
<label class="form-label" for="edit-petani-desa">Desa/Kelurahan<span class="text-danger">*</span></label>
<select class="form-control @error('desa_kelurahan_id') is-invalid @enderror" id="edit-petani-desa" name="desa_kelurahan_id" >
<option value="">Pilih Desa/Kelurahan</option>
@if($petani->desaKelurahan)
<option value="{{ $petani->desaKelurahan->id }}" selected>{{ $petani->desaKelurahan->nama }}</option>
@endif
</select>
@error('desa_kelurahan_id')<div class="error invalid-feedback">{{ $message }}</div>@enderror
</div>
@@ -242,7 +254,7 @@
</div>
<div class="mt-4 text-end">
<a href="{{ route('petani.index') }}" class="btn btn-secondary me-2 wave-effect wave-light">Kembali</a>
<a href="{{ route('petani.index') }}#data-petani" class="btn btn-secondary me-2 wave-effect wave-light">Kembali</a>
<button type="submit" class="btn btn-success data-submit wave-effect wave-light"><i class="ti tabler-device-floppy me-2"></i> Simpan Profil</button>
</div>
@@ -253,6 +265,20 @@
</div>
</div>
<div class="modal fade" id="fotoProfilModal" tabindex="-1" aria-labelledby="fotoProfilModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="fotoProfilModalLabel">Foto Profil Petani</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Tutup"></button>
</div>
<div class="modal-body text-center">
<img id="fotoProfilBesar" class="img-fluid rounded-3" alt="Foto profil petani" style="max-height: 75vh; object-fit: contain;" />
</div>
</div>
</div>
</div>
<div class="modal fade" id="cropModal" tabindex="-1" aria-labelledby="cropModalLabel" aria-hidden="true" data-bs-backdrop="static" data-bs-keyboard="false">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
@@ -344,7 +370,11 @@
let croppieInstance = null;
$('#fotoPreview').on('click', function () {
$('#fotoProfilModal').on('show.bs.modal', function () {
$('#fotoProfilBesar').attr('src', $('#fotoPreview').attr('src'));
});
$('#btnGantiFoto').on('click', function () {
$('#foto').val('');
$('#foto').click();
});
@@ -58,7 +58,7 @@
<hr class="my-2"> <h6 class="mb-1" style="font-size: 0.85rem;">Domisili</h6>
<p class="mb-0 text-muted" style="font-size: 0.75rem;">Alamat: {{ isset($petani) ? $petani->alamat : 'Alamat' }}</p>
<p class="mb-0 text-muted" style="font-size: 0.75rem; line-height: 1.3;">{{ isset($petani) ? $petani->desaKelurahan->nama : 'Desa/Kelurahan' }}, {{ $petani ? $petani->desaKelurahan->kecamatan->nama : 'Kecamatan' }}, {{ $petani ? $petani->desaKelurahan->kecamatan->kabupatenKota->nama : 'Kabupaten/Kota' }}, {{ $petani ? $petani->desaKelurahan->kecamatan->kabupatenKota->provinsi->nama : 'Provinsi' }}</p>
<p class="mb-0 text-muted" style="font-size: 0.75rem; line-height: 1.3;">{{ isset($petani) ? $petani->desaKelurahan?->nama ?? 'Belum diisi' : 'Desa/Kelurahan' }}, {{ $petani ? $petani->desaKelurahan?->kecamatan?->nama ?? 'Belum diisi' : 'Kecamatan' }}, {{ $petani ? $petani->desaKelurahan?->kecamatan?->kabupatenKota?->nama ?? 'Belum diisi' : 'Kabupaten/Kota' }}, {{ $petani ? $petani->desaKelurahan?->kecamatan?->kabupatenKota?->provinsi?->nama ?? 'Belum diisi' : 'Provinsi' }}</p>
</div>
</div>
</div>
@@ -66,33 +66,11 @@
<div class="col-md-8 d-flex flex-column">
@php
$skorLahanTanaman = isset($petani) ? $petani->skorLahanTanaman() : null;
$skorPraktikBudidaya = isset($petani) ? $petani->skorPraktikBudidaya() : null;
$skorPendidikan = isset($petani) ? $petani->skorPendidikan() : null;
$skorSosialEkonomiSegmen = isset($petani) ? $petani->skorSosialEkonomi() : null;
$skorAksesPasarSegmen = isset($petani) ? $petani->skorAksesPasar() : null;
$skorSegmenPetani = collect([
$skorLahanTanaman,
$skorPraktikBudidaya,
$skorPendidikan,
$skorSosialEkonomiSegmen,
$skorAksesPasarSegmen,
])->filter(fn ($value) => !is_null($value))->sum();
$skorSegmenPetani = $skorSegmenPetani > 0 ? round($skorSegmenPetani, 1) : null;
$segmenPetani = is_null($skorSegmenPetani) ? null : match (true) {
$skorSegmenPetani >= 4.0 => 'Penggerak',
$skorSegmenPetani >= 3.0 => 'Perintis',
$skorSegmenPetani >= 2.0 => 'Pemerhati',
$skorSegmenPetani >= 1.0 => 'Pemula',
default => 'Belum tersegmentasi',
};
$lahanPetani = isset($petani) && \Illuminate\Support\Facades\Schema::hasTable('map_lahan')
? $petani->lahans()->get()
: collect();
$totalLuasLahan = $petani ? $lahanPetani->where('komoditas_id', $petani->komoditas_id)->sum('luas_lahan') : 0;
$totalLuasLahanHa = $totalLuasLahan / 10000;
$totalLuasLahanHa = $petani ? $lahanPetani->where('komoditas_id', $petani->komoditas_id)->sum('luas_lahan') : 0;
$lahanKomoditasUtamaIds = $petani
? $lahanPetani->where('komoditas_id', $petani->komoditas_id)->pluck('id')
: collect();
@@ -100,7 +78,6 @@
? \App\Models\Pertanian\Tanaman::whereIn('lahan_id', $lahanKomoditasUtamaIds)->count()
: 0;
$pendidikanLabel = isset($petani) ? $petani->pendidikan_terakhir?->label() : null;
@endphp
<div class="card mb-2 flex-grow-1">
@@ -112,8 +89,8 @@
<p class="mb-0 text-muted" style="font-size: 0.75rem;">Segmen Petani</p>
<h5 class="mb-0">{{ $petani?->segmen() ?? '-' }}</h5>
<p class="mb-0" style="font-size: 0.75rem;">
@if(!is_null($petani?->skor))
<span class="me-1">Skor {{ number_format($petani?->skor->skor_kesiapan_adopsi, 1) }}</span>
@if(!is_null($petani?->skor?->skor_kesiapan_adopsi))
<span class="me-1">Skor {{ number_format($petani->skor->skor_kesiapan_adopsi, 1, ',', '.') }}/5</span>
@else
<span class="me-1">Belum terisi</span>
@endif
@@ -139,7 +116,7 @@
</h5>
<p class="mb-0" style="font-size: 0.75rem;">
@if(!is_null($skorLahanTanaman))
<span class="me-1">{{ number_format($totalTanaman, 0, ',', '.') }} tanaman {{ $petani?->komoditas->nama }}</span>
<span class="me-1">{{ number_format($totalTanaman, 0, ',', '.') }} tanaman {{ $petani?->komoditas?->nama ?? 'Belum diisi' }}</span>
@else
<span class="me-1">Belum terisi</span>
@endif
@@ -187,35 +164,19 @@
<div class="d-flex justify-content-between align-items-center card-widget-1 border-end h-100 pe-3">
@php
$skorPraktik = isset($petani?->skor) ? $petani?->skor->skor_d2_sistem_praktik_budidaya : null;
if (!is_null($skorPraktik)) {
if ($skorPraktik >= 4) {
$praktik = 'Menjalankan GAP (' . number_format($skorPraktik, 1, ',', '.') . ')';
} else if ($skorPraktik >= 2) {
$praktik = 'Menjalankan sebagian GAP (' . number_format($skorPraktik, 1, ',', '.') . ')';
} else {
$praktik = 'Belum menjalankan GAP (' . number_format($skorPraktik, 1, ',', '.') . ')';
}
}
@endphp
@php
$produktivitas = isset($petani?->surveyProduktivitas) ? $petani?->surveyProduktivitas : null;
if (!is_null($produktivitas)) {
$produktivitas = $produktivitas->where('komoditas_id', $petani->komoditas_id);
}
$labelPraktik = match (true) {
is_null($skorPraktik) => null,
$skorPraktik >= 4 => 'Penuh GAP',
$skorPraktik >= 2 => 'Sebagian GAP',
default => 'Belum GAP',
};
@endphp
<div>
<p class="mb-0 text-muted" style="font-size: 0.75rem;">Praktik Budidaya</p>
<h5 class="mb-0">
@if($produktivitas && count($produktivitas) > 0)
{{ number_format($produktivitas->sum('kuantitas'), 0, ',', '.') }}
<small>{{ $produktivitas->first()->satuan == 'ha_tahun' ? 'Ha/Tahun' : 'Pohon/Tahun' }}</small>
@else
-
@endif
</h5>
<h5 class="mb-0">{{ $labelPraktik ?? '-' }}</h5>
<p class="mb-0" style="font-size: 0.75rem;">
@if(!is_null($skorPraktik))
<span class="me-1">{{ $praktik }}</span>
<span class="me-1">Skor {{ number_format($skorPraktik, 1, ',', '.') }}/5</span>
@else
<span class="me-1">Belum terisi</span>
@endif
@@ -241,13 +202,15 @@
$pendapatanKomoditas = isset($sosialEkonomi) ? $sosialEkonomi->pendapatanKomoditas : null;
@endphp
@php
$rekeningValue = $sosialEkonomi?->status_rekening_tabungan?->value;
$kreditValue = $sosialEkonomi?->pengalaman_kredit_kebun?->value;
$ekonomi = null;
if ($sosialEkonomi?->status_rekening_tabungan && $sosialEkonomi?->pengalaman_kredit_kebun) {
if ($sosialEkonomi?->status_rekening_tabungan > 1 && $sosialEkonomi?->pengalaman_kredit_kebun > 3) {
if ($rekeningValue && $kreditValue) {
if ($rekeningValue > 1 && $kreditValue > 3) {
$ekonomi = 'Memiliki rekening dan pengalaman kredit';
} elseif ($sosialEkonomi?->status_rekening_tabungan > 1 && $sosialEkonomi?->pengalaman_kredit_kebun <= 3) {
} elseif ($rekeningValue > 1 && $kreditValue <= 3) {
$ekonomi = 'Memiliki rekening';
} elseif ($sosialEkonomi?->status_rekening_tabungan == 1 && $sosialEkonomi?->pengalaman_kredit_kebun > 3) {
} elseif ($rekeningValue == 1 && $kreditValue > 3) {
$ekonomi = 'Memiliki pengalaman kredit';
} else {
$ekonomi = 'Tidak memiliki akses keuangan';
@@ -257,9 +220,9 @@
}
@endphp
<h5 class="mb-0">
@if(!is_null($pendapatanKomoditas))
{{ number_format($pendapatanKomoditas->sum('pendapatan'), 0, ',', '.') }}
<small>pendapatan (Rp)</small>
@if($pendapatanKomoditas && $pendapatanKomoditas->isNotEmpty())
Rp {{ number_format($pendapatanKomoditas->sum('pendapatan'), 0, ',', '.') }}
<small>/musim</small>
@else
-
@endif
@@ -303,30 +266,35 @@
@endphp
@php
$pengalaman = null;
if(!is_null($aksesPasar?->skor_pengalaman) && !is_null($aksesPasar->skor_kesadaran_premium)){
if($aksesPasar->skor_pengalaman > 1){
if ($aksesPasar->skor_kesadaran_premium == 5) {
$pengalaman = 'Akses saluran lain dan pasar premium';
} else {
$pengalaman = 'Akses saluran lain';
}
} else {
$pengalaman = 'Belum akses';
if (!is_null($aksesPasar?->skor_pengalaman) && !is_null($aksesPasar->skor_kesadaran_premium)) {
$pengalaman = match ($aksesPasar->skor_pengalaman) {
1 => 'Belum pernah coba saluran lain',
2 => 'Pernah, kini tidak ada akses',
3 => 'Pernah, sempat berhenti',
4 => 'Pernah, sesekali masih jual',
5 => 'Pernah & masih punya akses',
default => null,
};
if (!is_null($pengalaman) && $aksesPasar->skor_kesadaran_premium == 5) {
$pengalaman .= ' · kenal pasar premium';
}
}
@endphp
<h5 class="mb-0">
@if(!is_null($hargaJual))
{{ number_format($hargaJual->harga_panen_terakhir, 0, ',', '.') }}
<small>{{ $hargaJual->bentuk_produk }}</small>
Rp {{ number_format($hargaJual->harga_panen_terakhir, 0, ',', '.') }}
@else
-
@endif
</h5>
<p class="mb-0" style="font-size: 0.75rem;">
<p class="mb-0 text-truncate" style="font-size: 0.75rem;" title="{{ $hargaJual?->bentuk_produk }} — {{ $pengalaman }}">
@if(!is_null($hargaJual))
<span class="me-1">{{ $hargaJual->bentuk_produk }}</span>
@endif
@if(!is_null($aksesPasar?->skor_pengalaman) && !is_null($aksesPasar->skor_kesadaran_premium))
<span class="me-1">{{ $pengalaman }}</span>
@else
<span class="me-1">{{ $hargaJual ? '·' : '' }} {{ $pengalaman }}</span>
@elseif(is_null($hargaJual))
<span class="me-1">Belum terisi</span>
@endif
</p>
@@ -1,44 +1,292 @@
@extends('layout.main')
@push('style')
<style>
#data-petani {
scroll-margin-top: 7rem;
}
.petani-row {
cursor: pointer;
}
.indicator-filter-row {
padding-bottom: 1.5rem;
padding-top: 1.5rem;
}
.indicator-filter-bar {
display: block;
}
.indicator-filter-line {
align-items: center;
display: flex;
flex-wrap: wrap;
gap: .5rem;
}
.indicator-filter-section-label {
align-items: center;
display: flex;
font-weight: 500;
margin-right: .5rem;
white-space: nowrap;
}
.indicator-filter-bar .form-select,
.indicator-filter-bar .btn {
min-height: 31px;
}
.indicator-filter-bar .form-select {
background-color: var(--bs-paper-bg);
border-color: var(--bs-border-color);
border-radius: var(--bs-border-radius);
color: var(--bs-body-color);
}
.indicator-filter-bar .btn {
white-space: nowrap;
}
.indicator-filter-bar .form-select:focus {
border-color: rgba(40, 167, 69, .55);
box-shadow: 0 0 0 .2rem rgba(40, 167, 69, .12);
}
.indicator-filter-static {
align-items: center;
background-color: var(--bs-paper-bg);
border: 1px solid var(--bs-border-color);
border-radius: var(--bs-border-radius);
color: var(--bs-body-color);
display: flex;
font-size: .8125rem;
min-height: 31px;
padding: .25rem .75rem;
}
.indicator-filter-bar .input-group-sm {
width: 240px;
}
@media (max-width: 767.98px) {
.indicator-filter-line {
display: grid;
grid-template-columns: 1fr;
width: 100%;
}
.indicator-filter-section-label {
margin-right: 0;
}
.indicator-filter-bar .btn {
justify-content: center;
width: 100%;
}
}
</style>
@endpush
@section('content')
<div class="container-xxl flex-grow-1 container-p-y">
<div class="d-flex flex-row gap-3 mb-4">
<div class="w-25">
<div class="container-fluid flex-grow-1 container-p-y">
<div class="row g-3 mb-4">
<div class="col-12 col-md-4">
<x-summary-card
title="Petani"
count="{{ $petani->count() }}"
subtitle="{{ $petani->where('jenis_kelamin', 1)->count() }} Laki-laki, {{ $petani->where('jenis_kelamin', 0)->count() }} Perempuan"
count="{{ number_format($totalPetani ?? 0, 0, ',', '.') }}"
subtitle="{{ number_format($totalPetaniLakiLaki ?? 0, 0, ',', '.') }} Laki-laki, {{ number_format($totalPetaniPerempuan ?? 0, 0, ',', '.') }} Perempuan"
icon="user-check"
color="primary"
/>
</div>
<div class="w-25">
<div class="col-12 col-md-4">
<x-summary-card
title="Lahan & Tanaman"
count="{{ number_format(($totalLuasLahan ?? 0) / 10000, 2, ',', '.') }} ha"
subtitle="{{ $totalTanaman ?? 0 }} tanaman dari {{ $komoditas?->count() }} komoditas utama"
count="{{ number_format($totalLuasLahan ?? 0, 2, ',', '.') }} ha"
subtitle="{{ number_format($totalLahan ?? 0, 0, ',', '.') }} lahan, {{ number_format($totalTanaman ?? 0, 0, ',', '.') }} tanaman"
icon="trees"
color="success"
/>
</div>
<div class="w-25">
<div class="col-12 col-md-4">
<x-summary-card
title="Segmentasi"
count="{{ $skor->where('segmen', 'Penggerak')->count() }} Penggerak"
subtitle="{{ $skor->where('segmen', 'Perintis')->count() }} Perintis, {{ $skor->where('segmen', 'Pemerhati')->count() }} Pemerhati, {{ $skor->where('segmen', 'Pemula')->count() }} Pemula"
count="{{ number_format((int) ($skor->firstWhere('segmen', 'Penggerak')->total ?? 0), 0, ',', '.') }} Penggerak"
subtitle="{{ number_format((int) ($skor->firstWhere('segmen', 'Perintis')->total ?? 0), 0, ',', '.') }} Perintis, {{ number_format((int) ($skor->firstWhere('segmen', 'Pemerhati')->total ?? 0), 0, ',', '.') }} Pemerhati, {{ number_format((int) ($skor->firstWhere('segmen', 'Pemula')->total ?? 0), 0, ',', '.') }} Pemula"
icon="star"
color="info"
/>
</div>
</div>
<div class="card">
<div class="row g-6 indicator-filter-row">
<div class="col-12">
<div class="card card-border-shadow-primary">
<div class="card-body py-3">
<form method="GET" action="{{ route('petani.index') }}" id="petani-filter-form">
<input type="hidden" name="per_page" value="{{ request('per_page', 10) }}">
<div class="indicator-filter-bar" id="indicator-filter-bar" aria-label="Filter data petani">
<div class="indicator-filter-line">
<div class="indicator-filter-section-label">
<i class="icon-base ti tabler-map-2 me-2"></i>
<span>Wilayah</span>
</div>
<div class="filter-item">
<input
type="hidden"
name="kabupaten_kota_id"
value="{{ $filters['kabupaten_kota_id'] }}"
>
<div
id="filter-kabupaten"
class="form-control form-control-sm indicator-filter-static"
data-filter-key="kabupaten"
data-filter-value="{{ $filters['kabupaten_kota_id'] }}"
aria-label="Filter Kabupaten"
role="text"
>
{{ preg_replace('/^(kabupaten|kota)\s+/i', '', $selectedKabupaten?->nama ?? 'Ngada') }}
</div>
</div>
<div class="filter-item">
<select
name="kecamatan_id"
id="filter-kecamatan"
class="form-select form-select-sm indicator-filter-select"
data-filter-key="kecamatan"
aria-label="Filter Kecamatan"
@disabled(empty($filters['kabupaten_kota_id']))
>
<option value="">Semua Kecamatan</option>
@foreach($kecamatanOptions as $option)
<option value="{{ $option->id }}" @selected((string) $filters['kecamatan_id'] === (string) $option->id)>
{{ $option->nama }}
</option>
@endforeach
</select>
</div>
<div class="filter-item">
<select
name="desa_kelurahan_id"
id="filter-desa"
class="form-select form-select-sm indicator-filter-select"
data-filter-key="desa"
aria-label="Filter Desa"
@disabled(empty($filters['kecamatan_id']))
>
<option value="">Semua Desa</option>
@foreach($desaOptions as $option)
<option value="{{ $option->id }}" @selected((string) $filters['desa_kelurahan_id'] === (string) $option->id)>
{{ $option->nama }}
</option>
@endforeach
</select>
</div>
</div>
<hr class="my-3">
<div class="indicator-filter-line">
<div class="indicator-filter-section-label">
<i class="icon-base ti tabler-adjustments-horizontal me-2"></i>
<span>Data</span>
</div>
<div class="filter-item">
<select
name="komoditas_id"
id="filter-komoditas"
class="form-select form-select-sm indicator-filter-select"
data-filter-key="komoditas"
aria-label="Filter Komoditas"
>
<option value="">Semua Komoditas</option>
@foreach($komoditasOptions as $option)
<option value="{{ $option->id }}" @selected((string) $filters['komoditas_id'] === (string) $option->id)>
{{ $option->nama }}
</option>
@endforeach
</select>
</div>
<div class="filter-item">
<select
name="sort_by"
id="filter-sort-by"
class="form-select form-select-sm indicator-filter-select"
data-filter-key="sort_by"
aria-label="Urutkan data petani"
>
<option value="">Urut Terbaru</option>
<option value="nama_asc" @selected($filters['sort_by'] === 'nama_asc')>Nama A-Z</option>
<option value="nama_desc" @selected($filters['sort_by'] === 'nama_desc')>Nama Z-A</option>
<option value="luas_lahan_desc" @selected($filters['sort_by'] === 'luas_lahan_desc')>Luas Lahan Terbesar</option>
<option value="luas_lahan_asc" @selected($filters['sort_by'] === 'luas_lahan_asc')>Luas Lahan Terkecil</option>
</select>
</div>
<div class="filter-item">
<div class="input-group input-group-sm">
<input
type="text"
name="search"
class="form-control"
placeholder="Cari nama / KTP..."
value="{{ $filters['search'] }}"
aria-label="Cari nama atau KTP"
>
<button type="submit" class="btn btn-outline-secondary" aria-label="Cari data petani">
<i class="icon-base ti tabler-search"></i>
</button>
</div>
</div>
<button
type="submit"
class="btn btn-outline-secondary btn-sm"
aria-label="Terapkan filter data petani"
>
<i class="icon-base ti tabler-filter me-1"></i>
Filter
</button>
<a
href="{{ route('petani.index') }}"
class="btn btn-outline-secondary btn-sm"
aria-label="Reset filter data petani"
>
<i class="icon-base ti tabler-refresh me-1"></i>
Reset
</a>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<div class="card" id="data-petani">
<div class="card-header border-bottom">
<h5 class="card-title mb-0">Data Petani</h5>
</div>
<div class="card-body border-bottom pt-4">
<form method="GET" action="{{ route('petani.index') }}" class="d-flex justify-content-between align-items-center row gap-3 gap-md-0">
<form method="GET" action="{{ route('petani.index') }}" class="row g-3 align-items-end">
<input type="hidden" name="komoditas_id" value="{{ $filters['komoditas_id'] }}">
<input type="hidden" name="kabupaten_kota_id" value="{{ $filters['kabupaten_kota_id'] }}">
<input type="hidden" name="kecamatan_id" value="{{ $filters['kecamatan_id'] }}">
<input type="hidden" name="desa_kelurahan_id" value="{{ $filters['desa_kelurahan_id'] }}">
<input type="hidden" name="search" value="{{ $filters['search'] }}">
<input type="hidden" name="sort_by" value="{{ $filters['sort_by'] }}">
<div class="col-md-auto">
<div class="d-flex align-items-center">
<label class="me-2 text-nowrap">Show</label>
@@ -49,11 +297,7 @@
<label class="ms-2 text-nowrap">entries</label>
</div>
</div>
<div class="col-md-auto d-flex gap-2">
<div class="input-group input-group-sm w-auto">
<input type="text" name="search" class="form-control" placeholder="Cari Nama..." value="{{ request('search') }}">
<button type="submit" class="btn btn-outline-primary"><i class="ti tabler-search"></i></button>
</div>
<div class="col-md-auto ms-md-auto">
<a href="{{ route('petani.create') }}" class="btn btn-sm btn-primary">
<i class="ti tabler-plus me-1"></i> Tambah
</a>
@@ -67,23 +311,39 @@
<tr>
<th>No</th>
<th>Nama</th>
<th>Jenis Kelamin</th>
<th>Usia</th>
<th>Desa/Kelurahan</th>
<th>Komoditas Utama</th>
<th>Luas Lahan</th>
@if($showFasilitatorColumn)
<th>Fasilitator</th>
@endif
<th>Segmentasi</th>
<th>Aksi</th>
</tr>
</thead>
<tbody>
@forelse($petani as $index => $item)
<tr>
<tr class="petani-row">
<td>{{ $loop->iteration }}</td>
<td><span class="text-heading">{{ $item->nama }}</span></td>
<td><span class="text-muted">{{ $item->jenis_kelamin ? 'Laki-laki' : 'Perempuan' }}</span></td>
<td><span class="text-muted">{{ $item->umur }} tahun</span></td>
<td>
<a href="{{ route('petani.edit', $item->id) }}" class="text-heading d-block">{{ $item->nama }}</a>
<small class="text-muted">
{{ $item->jenis_kelamin ? 'Laki-laki' : 'Perempuan' }}
@if(! is_null($item->umur))
, {{ $item->umur }} tahun
@endif
</small>
</td>
<td><span class="text-muted">{{ $item->desaKelurahan?->nama }}, {{ $item->desaKelurahan?->kecamatan?->nama }}, {{ $item->desaKelurahan?->kecamatan?->kabupatenKota?->nama }}, {{ $item->desaKelurahan?->kecamatan?->kabupatenKota?->provinsi?->nama }}</span></td>
<td>{{ $item->komoditas?->nama }}</td>
<td>
<span class="text-muted">
{{ number_format((float) ($item->total_luas_lahan ?? 0), 2, ',', '.') }} ha
</span>
</td>
@if($showFasilitatorColumn)
<td><span class="text-muted">{{ $item->creator?->name ?? '-' }}</span></td>
@endif
<td>
@if($item->segmen() == 'Penggerak')
<span class="badge bg-label-success">Penggerak</span>
@@ -104,7 +364,7 @@
</td>
</tr>
@empty
<tr><td colspan="6" class="text-center py-4">Tidak ada data ditemukan.</td></tr>
<tr><td colspan="{{ $showFasilitatorColumn ? 9 : 8 }}" class="text-center py-4">Tidak ada data ditemukan.</td></tr>
@endforelse
</tbody>
</table>
@@ -122,6 +382,63 @@
const csrfToken = "{{ csrf_token() }}";
$(document).ready(function () {
const kecamatanUrlTemplate = '{{ route("kecamatan.bykabupatenkota", ":id") }}';
const desaUrlTemplate = '{{ route("desakelurahan.bykecamatan", ":id") }}';
const $kabupatenFilter = $('#filter-kabupaten');
const $kecamatanFilter = $('#filter-kecamatan');
const $desaFilter = $('#filter-desa');
function resetSelect($select, placeholder, disabled = true) {
$select.empty().append(`<option value="">${placeholder}</option>`);
$select.prop('disabled', disabled);
}
function fillSelect($select, data, selectedValue, placeholder) {
resetSelect($select, placeholder, false);
data.forEach(function (item) {
const option = new Option(item.nama, item.id, false, String(selectedValue || '') === String(item.id));
$select.append(option);
});
}
$kabupatenFilter.on('change', function () {
const kabupatenId = $(this).val();
resetSelect($kecamatanFilter, 'Semua Kecamatan');
resetSelect($desaFilter, 'Semua Desa');
if (!kabupatenId) {
return;
}
$.get(kecamatanUrlTemplate.replace(':id', kabupatenId), function (data) {
fillSelect($kecamatanFilter, data, '', 'Semua Kecamatan');
});
});
$kecamatanFilter.on('change', function () {
const kecamatanId = $(this).val();
resetSelect($desaFilter, 'Semua Desa');
if (!kecamatanId) {
return;
}
$.get(desaUrlTemplate.replace(':id', kecamatanId), function (data) {
fillSelect($desaFilter, data, '', 'Semua Desa');
});
});
$(document).on('click', '.petani-row', function (event) {
if ($(event.target).closest('a, button, input, select, textarea, label').length) {
return;
}
window.location.href = this.querySelector('.edit-record').href;
});
$(document).on('click', '.delete-record', function () {
const id = $(this).data('id');
const name = $(this).data('nama');
@@ -517,7 +517,7 @@
<div class="row mt-3">
<div class="col-12 text-end">
<a href="{{ route('petani.index') }}" class="btn btn-secondary me-2 wave-effect wave-light">Kembali</a>
<a href="{{ route('petani.index') }}#data-petani" class="btn btn-secondary me-2 wave-effect wave-light">Kembali</a>
<button type="submit" class="btn btn-success data-submit wave-effect wave-light"><i class="ti tabler-device-floppy me-2"></i> Simpan Lahan</button>
</div>
</div>
@@ -406,8 +406,8 @@
<div class="row mt-3">
<div class="col-12 text-end">
<a href="{{ route('petani.index') }}" class="btn btn-secondary me-2 wave-effect wave-light">Kembali</a>
<button id="delete-lahan"type="button" class="btn btn-danger data-submit wave-effect wave-light"><i class="ti tabler-trash me-2"></i> Hapus Lahan</button>
<a href="{{ route('petani.index') }}#data-petani" class="btn btn-secondary me-2 wave-effect wave-light">Kembali</a>
<button type="submit" class="btn btn-success data-submit wave-effect wave-light"><i class="ti tabler-device-floppy me-2"></i> Simpan Lahan</button>
</div>
</div>
@@ -86,6 +86,7 @@
</div>
<div class="mb-3">
<a href="{{ route('petani.index') }}#data-petani" class="btn btn-secondary me-2">Kembali</a>
<button type="submit" class="btn btn-primary">Simpan Perubahan</button>
</div>
</form>
@@ -77,6 +77,7 @@
</table>
</div>
<div class="mt-4 text-end">
<a href="{{ route('petani.index') }}#data-petani" class="btn btn-secondary me-2">Kembali</a>
<button type="submit" class="btn btn-success">
<i class="icon-base ti tabler-check me-1_5 icon-sm"></i>
Simpan
@@ -167,10 +167,13 @@
Tambah Pelatihan
</button>
<button type="submit" class="btn btn-success">
<i class="icon-base ti tabler-check me-1_5 icon-sm"></i>
Simpan Riwayat Pelatihan
</button>
<div class="d-flex flex-wrap justify-content-end gap-2">
<a href="{{ route('petani.index') }}#data-petani" class="btn btn-secondary">Kembali</a>
<button type="submit" class="btn btn-success">
<i class="icon-base ti tabler-check me-1_5 icon-sm"></i>
Simpan Riwayat Pelatihan
</button>
</div>
</div>
</form>
</div>
@@ -347,10 +350,13 @@
Tambah Sertifikasi
</button>
<button type="submit" class="btn btn-success">
<i class="icon-base ti tabler-check me-1_5 icon-sm"></i>
Simpan Sertifikasi Keahlian
</button>
<div class="d-flex flex-wrap justify-content-end gap-2">
<a href="{{ route('petani.index') }}#data-petani" class="btn btn-secondary">Kembali</a>
<button type="submit" class="btn btn-success">
<i class="icon-base ti tabler-check me-1_5 icon-sm"></i>
Simpan Sertifikasi Keahlian
</button>
</div>
</div>
</form>
</div>
@@ -499,6 +499,7 @@
</div>
</div>
<div class="mt-4 text-end">
<a href="{{ route('petani.index') }}#data-petani" class="btn btn-secondary me-2">Kembali</a>
<button type="submit" class="btn btn-success">
Simpan
</button>
@@ -530,6 +530,7 @@
</div>
</div>
<div class="mt-4 text-end">
<a href="{{ route('petani.index') }}#data-petani" class="btn btn-secondary me-2">Kembali</a>
<button type="submit" class="btn btn-success">
<i class="icon-base ti tabler-check me-1_5 icon-sm"></i>
Simpan
@@ -1039,6 +1040,7 @@
</div>
</div>
<div class="mt-4 text-end">
<a href="{{ route('petani.index') }}#data-petani" class="btn btn-secondary me-2">Kembali</a>
<button type="submit" class="btn btn-success">
<i class="icon-base ti tabler-check me-1_5 icon-sm"></i>
Simpan
@@ -1352,6 +1354,7 @@
</div>
</div>
<div class="mt-4 text-end">
<a href="{{ route('petani.index') }}#data-petani" class="btn btn-secondary me-2">Kembali</a>
<button type="submit" class="btn btn-success">
<i class="icon-base ti tabler-check me-1_5 icon-sm"></i>
Simpan
@@ -1542,6 +1545,7 @@
</div>
</div>
<div class="mt-4 text-end">
<a href="{{ route('petani.index') }}#data-petani" class="btn btn-secondary me-2">Kembali</a>
<button type="submit" class="btn btn-success">
<i class="icon-base ti tabler-check me-1_5 icon-sm"></i>
Simpan
@@ -1678,9 +1682,12 @@
Tambah Komoditas
</button>
<button type="submit" class="btn btn-success">
Simpan Produktivitas
</button>
<div class="d-flex flex-wrap justify-content-end gap-2">
<a href="{{ route('petani.index') }}#data-petani" class="btn btn-secondary">Kembali</a>
<button type="submit" class="btn btn-success">
Simpan
</button>
</div>
</div>
</form>
</div>
@@ -1725,6 +1732,7 @@
</div>
</div>
<div class="mt-4 text-end">
<a href="{{ route('petani.index') }}#data-petani" class="btn btn-secondary me-2">Kembali</a>
<button type="submit" class="btn btn-success">
<i class="icon-base ti tabler-check me-1_5 icon-sm"></i>
Simpan
@@ -399,6 +399,7 @@
</div>
<div class="mt-4 text-end">
<a href="{{ route('petani.index') }}#data-petani" class="btn btn-secondary me-2">Kembali</a>
<button type="submit" class="btn btn-success">
<i class="icon-base ti tabler-check me-1_5 icon-sm"></i>
Simpan
@@ -567,6 +568,7 @@
</div>
<div class="mt-4 text-end">
<a href="{{ route('petani.index') }}#data-petani" class="btn btn-secondary me-2">Kembali</a>
<button type="submit" class="btn btn-success">
<i class="icon-base ti tabler-check me-1_5 icon-sm"></i>
Simpan
@@ -696,6 +698,7 @@
</div>
<div class="mt-4 text-end">
<a href="{{ route('petani.index') }}#data-petani" class="btn btn-secondary me-2">Kembali</a>
<button type="submit" class="btn btn-success">
<i class="icon-base ti tabler-check me-1_5 icon-sm"></i>
Simpan
@@ -915,6 +918,7 @@
</div>
<div class="mt-4 text-end">
<a href="{{ route('petani.index') }}#data-petani" class="btn btn-secondary me-2">Kembali</a>
<button type="submit" class="btn btn-success">
<i class="icon-base ti tabler-check me-1_5 icon-sm"></i>
Simpan
@@ -1043,6 +1047,7 @@
</div>
<div class="mt-4 text-end">
<a href="{{ route('petani.index') }}#data-petani" class="btn btn-secondary me-2">Kembali</a>
<button type="submit" class="btn btn-success">
<i class="icon-base ti tabler-check me-1_5 icon-sm"></i>
Simpan
@@ -398,6 +398,7 @@
</div>
<div class="mt-4 text-end">
<a href="{{ route('petani.index') }}#data-petani" class="btn btn-secondary me-2">Kembali</a>
<button type="submit" class="btn btn-success">
<i class="icon-base ti tabler-check me-1_5 icon-sm"></i>
Simpan
@@ -566,6 +567,7 @@
</div>
<div class="mt-4 text-end">
<a href="{{ route('petani.index') }}#data-petani" class="btn btn-secondary me-2">Kembali</a>
<button type="submit" class="btn btn-success">
<i class="icon-base ti tabler-check me-1_5 icon-sm"></i>
Simpan
@@ -694,6 +696,7 @@
</div>
<div class="mt-4 text-end">
<a href="{{ route('petani.index') }}#data-petani" class="btn btn-secondary me-2">Kembali</a>
<button type="submit" class="btn btn-success">
<i class="icon-base ti tabler-check me-1_5 icon-sm"></i>
Simpan
@@ -912,6 +915,7 @@
</div>
<div class="mt-4 text-end">
<a href="{{ route('petani.index') }}#data-petani" class="btn btn-secondary me-2">Kembali</a>
<button type="submit" class="btn btn-success">
<i class="icon-base ti tabler-check me-1_5 icon-sm"></i>
Simpan
@@ -1039,6 +1043,7 @@
</div>
<div class="mt-4 text-end">
<a href="{{ route('petani.index') }}#data-petani" class="btn btn-secondary me-2">Kembali</a>
<button type="submit" class="btn btn-success">
<i class="icon-base ti tabler-check me-1_5 icon-sm"></i>
Simpan
@@ -85,7 +85,7 @@
</div>
<div class="mt-4 text-end">
<a href="{{ route('petani.index') }}" class="btn btn-label-secondary me-2">Kembali</a>
<a href="{{ route('petani.index') }}#data-petani" class="btn btn-label-secondary me-2">Kembali</a>
</div>
</form>
</div>
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,51 @@
@php
$notificationTypes = [
'success' => 'success',
'error' => 'danger',
'warning' => 'warning',
'info' => 'info',
'status' => 'info',
'message' => 'info',
];
$hasNotifications = $errors->any() || collect(array_keys($notificationTypes))->contains(fn ($key) => session()->has($key));
@endphp
@if($hasNotifications)
<div id="kelompok-tani-notifications">
@foreach($notificationTypes as $key => $type)
@if(session()->has($key))
<div class="alert alert-{{ $type }} alert-dismissible" role="alert">
{{ session($key) }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Tutup"></button>
</div>
@endif
@endforeach
@if($errors->any())
<div class="alert alert-danger alert-dismissible" role="alert">
<ul class="mb-0">
@foreach($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Tutup"></button>
</div>
@endif
</div>
@push('scripts')
<script>
window.addEventListener('load', function () {
const hasErrors = @json($errors->any() || session()->has('error'));
if (!hasErrors && window.location.hash.startsWith('#section-')) {
const target = document.getElementById(window.location.hash.substring(1));
if (target && target.closest('.kelompok-tani-page') && target.getClientRects().length) {
target.scrollIntoView({ block: 'start' });
return;
}
}
document.getElementById('kelompok-tani-notifications')?.scrollIntoView({ block: 'start' });
});
</script>
@endpush
@endif
@@ -0,0 +1,252 @@
@extends('layout.main')
@section('content')
<div class="container-xxl flex-grow-1 container-p-y kelompok-tani-page">
@include('produktivitas.survey-kelompok-tani.header', ['kelompokTani' => null])
<div class="row">
<div class="col-md-12">
<div class="card mb-6">
<div class="card-body pt-6">
<form novalidate id="addKelompokTaniForm" action="{{ route('kelompok-tani.store') }}" method="POST">
@csrf
<h5 class="pb-4 border-bottom mb-4">Data Responden</h5>
<div class="row">
<div class="col-md-4 mb-4 form-control-validation">
<label class="form-label" for="add-kt-nama-responden">Nama Responden<span class="text-danger">*</span></label>
<input type="text" class="form-control @error('nama_responden') is-invalid @enderror" id="add-kt-nama-responden" name="nama_responden" required
placeholder="Cth: John Doe" value="{{ old('nama_responden') }}" />
@error('nama_responden')<div class="error invalid-feedback">{{ $message }}</div>@enderror
</div>
<div class="col-md-4 mb-4 form-control-validation">
<label class="form-label" for="add-kt-jabatan-responden">Jabatan Responden</label>
<input type="text" class="form-control @error('jabatan_responden') is-invalid @enderror" id="add-kt-jabatan-responden" name="jabatan_responden"
placeholder="Cth: Ketua Kelompok" value="{{ old('jabatan_responden') }}" />
@error('jabatan_responden')<div class="error invalid-feedback">{{ $message }}</div>@enderror
</div>
<div class="col-md-4 mb-4 form-control-validation">
<label class="form-label" for="add-kt-kontak-responden">Kontak Responden (No. HP)</label>
<input type="text" class="form-control @error('kontak_responden') is-invalid @enderror" id="add-kt-kontak-responden" name="kontak_responden"
placeholder="Cth: 081234567890" value="{{ old('kontak_responden') }}" />
@error('kontak_responden')<div class="error invalid-feedback">{{ $message }}</div>@enderror
</div>
</div>
<h5 class="pb-4 border-bottom mb-4 mt-4">Identitas Kelompok Tani</h5>
<div class="row">
<div class="col-md-6 mb-4 form-control-validation">
<label class="form-label" for="add-kt-nama-kelompok">Nama Kelompok Tani<span class="text-danger">*</span></label>
<input type="text" class="form-control @error('nama_kelompok') is-invalid @enderror" id="add-kt-nama-kelompok" name="nama_kelompok" required
placeholder="Cth: Kelompok Tani Maju Bersama" value="{{ old('nama_kelompok') }}" />
@error('nama_kelompok')<div class="error invalid-feedback">{{ $message }}</div>@enderror
</div>
<div class="col-md-3 mb-4 form-control-validation">
<label class="form-label" for="add-kt-dusun">Dusun</label>
<input type="text" class="form-control @error('dusun') is-invalid @enderror" id="add-kt-dusun" name="dusun"
placeholder="Cth: Dusun I" value="{{ old('dusun') }}" />
@error('dusun')<div class="error invalid-feedback">{{ $message }}</div>@enderror
</div>
<div class="col-md-3 mb-4 form-control-validation">
<label class="form-label" for="add-kt-tahun-berdiri">Tahun Berdiri</label>
<input type="number" class="form-control @error('tahun_berdiri') is-invalid @enderror" id="add-kt-tahun-berdiri" name="tahun_berdiri"
placeholder="Cth: 2015" min="1900" max="{{ date('Y') }}" value="{{ old('tahun_berdiri') }}" />
@error('tahun_berdiri')<div class="error invalid-feedback">{{ $message }}</div>@enderror
</div>
</div>
<div class="row">
<div class="col-md-3 mb-4 form-control-validation">
<label class="form-label" for="add-kt-provinsi">Provinsi<span class="text-danger">*</span></label>
<select class="form-select @error('provinsi_id') is-invalid @enderror" id="add-kt-provinsi" name="provinsi_id" required>
<option value="">Pilih Provinsi</option>
@foreach($provinsi as $p)
<option value="{{ $p->id }}" {{ old('provinsi_id') == $p->id ? 'selected' : '' }}>{{ $p->nama }}</option>
@endforeach
</select>
@error('provinsi_id')<div class="error invalid-feedback">{{ $message }}</div>@enderror
</div>
<div class="col-md-3 mb-4 form-control-validation">
<label class="form-label" for="add-kt-kabupaten">Kabupaten/Kota<span class="text-danger">*</span></label>
<select class="form-select @error('kabupaten_id') is-invalid @enderror" id="add-kt-kabupaten" name="kabupaten_id" required @disabled(!old('provinsi_id'))>
<option value="">Pilih Kabupaten/Kota</option>
@foreach($kabupatenOptions ?? [] as $option)
<option value="{{ $option->id }}" @selected(old('kabupaten_id') == $option->id)>{{ $option->nama }}</option>
@endforeach
</select>
@error('kabupaten_id')<div class="error invalid-feedback">{{ $message }}</div>@enderror
</div>
<div class="col-md-3 mb-4 form-control-validation">
<label class="form-label" for="add-kt-kecamatan">Kecamatan<span class="text-danger">*</span></label>
<select class="form-select @error('kecamatan_id') is-invalid @enderror" id="add-kt-kecamatan" name="kecamatan_id" required @disabled(!old('kabupaten_id'))>
<option value="">Pilih Kecamatan</option>
@foreach($kecamatanOptions ?? [] as $option)
<option value="{{ $option->id }}" @selected(old('kecamatan_id') == $option->id)>{{ $option->nama }}</option>
@endforeach
</select>
@error('kecamatan_id')<div class="error invalid-feedback">{{ $message }}</div>@enderror
</div>
<div class="col-md-3 mb-4 form-control-validation">
<label class="form-label" for="add-kt-desa">Desa/Kelurahan<span class="text-danger">*</span></label>
<select class="form-select @error('desa_kelurahan_id') is-invalid @enderror" id="add-kt-desa" name="desa_kelurahan_id" required @disabled(!old('kecamatan_id'))>
<option value="">Pilih Desa/Kelurahan</option>
@foreach($desaOptions ?? [] as $option)
<option value="{{ $option->id }}" @selected(old('desa_kelurahan_id') == $option->id)>{{ $option->nama }}</option>
@endforeach
</select>
@error('desa_kelurahan_id')<div class="error invalid-feedback">{{ $message }}</div>@enderror
</div>
</div>
<div class="row">
<div class="col-md-6 mb-4 form-control-validation">
<label class="form-label" for="add-kt-jenis-kelompok">Terdaftar/Dikenal Sebagai<span class="text-danger">*</span></label>
<select class="form-select @error('jenis_kelompok') is-invalid @enderror" id="add-kt-jenis-kelompok" name="jenis_kelompok" required>
<option value="">Pilih Jenis Kelompok</option>
<option value="Kelompok Tani (Poktan)" {{ old('jenis_kelompok') == 'Kelompok Tani (Poktan)' ? 'selected' : '' }}>Kelompok Tani (Poktan)</option>
<option value="Kelompok Wanita Tani (KWT)" {{ old('jenis_kelompok') == 'Kelompok Wanita Tani (KWT)' ? 'selected' : '' }}>Kelompok Wanita Tani (KWT)</option>
<option value="Gabungan Kelompok Tani (Gapoktan)" {{ old('jenis_kelompok') == 'Gabungan Kelompok Tani (Gapoktan)' ? 'selected' : '' }}>Gabungan Kelompok Tani (Gapoktan)</option>
<option value="Kelompok Pemuda Tani" {{ old('jenis_kelompok') == 'Kelompok Pemuda Tani' ? 'selected' : '' }}>Kelompok Pemuda Tani</option>
<option value="Asosiasi Petani" {{ old('jenis_kelompok') == 'Asosiasi Petani' ? 'selected' : '' }}>Asosiasi Petani</option>
<option value="Lainnya" {{ old('jenis_kelompok') == 'Lainnya' ? 'selected' : '' }}>Lainnya</option>
</select>
@error('jenis_kelompok')<div class="error invalid-feedback">{{ $message }}</div>@enderror
</div>
<div class="col-md-4 mb-4 form-control-validation" id="add-kt-jenis-kelompok-lainnya-wrapper" style="display: none;">
<label class="form-label" for="add-kt-jenis-kelompok-lainnya">Sebutkan Jenis Kelompok<span class="text-danger">*</span></label>
<input type="text" class="form-control @error('jenis_kelompok_lainnya') is-invalid @enderror" id="add-kt-jenis-kelompok-lainnya" name="jenis_kelompok_lainnya"
value="{{ old('jenis_kelompok_lainnya') }}" />
@error('jenis_kelompok_lainnya')<div class="error invalid-feedback">{{ $message }}</div>@enderror
</div>
<div class="col-md-4 mb-4 form-control-validation" id="add-kt-nama-gapoktan-wrapper" style="display: none;">
<label class="form-label" for="add-kt-nama-gapoktan">Nama Gapoktan</label>
<input type="text" class="form-control @error('nama_gapoktan') is-invalid @enderror" id="add-kt-nama-gapoktan" name="nama_gapoktan"
placeholder="Cth: Gapoktan Sumber Makmur" value="{{ old('nama_gapoktan') }}" />
@error('nama_gapoktan')<div class="error invalid-feedback">{{ $message }}</div>@enderror
</div>
</div>
<div class="row">
<div class="col-md-6 mb-4 form-control-validation">
<label class="form-label">Komoditas Utama</label>
<select id="add-kt-komoditas" name="komoditas_id[]" class="form-select @error('komoditas_id') is-invalid @enderror" multiple>
@foreach($komoditas as $k)
<option value="{{ $k->id }}" {{ collect(old('komoditas_id'))->contains($k->id) ? 'selected' : '' }}>{{ $k->nama }}</option>
@endforeach
</select>
@error('komoditas_id')<div class="error invalid-feedback">{{ $message }}</div>@enderror
</div>
</div>
@include('produktivitas.survey-kelompok-tani.pengurus-anggota', ['bagianKeanggotaan' => 'ringkasan', 'kelompokTani' => null])
<div class="mt-4 text-end">
<div class="d-inline-flex flex-wrap justify-content-end gap-2">
<a href="{{ route('kelompok-tani.index') }}" class="btn btn-secondary wave-effect wave-light">
Kembali
</a>
<button type="submit" class="btn btn-success data-submit wave-effect wave-light">
<i class="icon-base ti tabler-check me-1_5 icon-sm" aria-hidden="true"></i> Simpan
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection
@push('scripts')
<script>
'use strict';
$(document).ready(function () {
$('#add-kt-jenis-kelompok, #add-kt-komoditas').select2();
function toggleJenisKelompokFields() {
var jenisKelompok = $('#add-kt-jenis-kelompok').val();
$('#add-kt-jenis-kelompok-lainnya').prop('required', jenisKelompok === 'Lainnya').prop('disabled', jenisKelompok !== 'Lainnya');
if (jenisKelompok === 'Lainnya') {
$('#add-kt-jenis-kelompok-lainnya-wrapper').show();
} else {
$('#add-kt-jenis-kelompok-lainnya-wrapper').hide();
$('#add-kt-jenis-kelompok-lainnya').val('');
}
if (jenisKelompok === 'Gabungan Kelompok Tani (Gapoktan)') {
$('#add-kt-nama-gapoktan-wrapper').show();
} else {
$('#add-kt-nama-gapoktan-wrapper').hide();
$('#add-kt-nama-gapoktan').val('');
}
}
toggleJenisKelompokFields();
$('#add-kt-jenis-kelompok').on('change', toggleJenisKelompokFields);
$('#add-kt-provinsi').select2().on('change', function (e) {
e.preventDefault();
var provinsiId = $(this).val();
$('#add-kt-kabupaten').prop('disabled', !provinsiId).empty().append('<option value="">Pilih Kabupaten/Kota</option>').trigger('change');
$('#add-kt-kecamatan').prop('disabled', true).empty().append('<option value="">Pilih Kecamatan</option>').trigger('change');
$('#add-kt-desa').prop('disabled', true).empty().append('<option value="">Pilih Desa/Kelurahan</option>').trigger('change');
if (provinsiId) {
$.ajax({
url: '{{ route("kabupatenkota.byprovinsi", ":id") }}'.replace(':id', provinsiId),
method: 'GET',
success: function (data) {
$('#add-kt-kabupaten').prop('disabled', false);
data.forEach(function (kabupaten) {
$('#add-kt-kabupaten').append('<option value="' + kabupaten.id + '">' + kabupaten.nama + '</option>');
});
}
});
}
});
$('#add-kt-kabupaten').select2().on('change', function (e) {
e.preventDefault();
var kabupatenId = $(this).val();
$('#add-kt-kecamatan').prop('disabled', !kabupatenId).empty().append('<option value="">Pilih Kecamatan</option>').trigger('change');
$('#add-kt-desa').prop('disabled', true).empty().append('<option value="">Pilih Desa/Kelurahan</option>').trigger('change');
if (kabupatenId) {
$.ajax({
url: '{{ route("kecamatan.bykabupatenkota", ":id") }}'.replace(':id', kabupatenId),
method: 'GET',
success: function (data) {
$('#add-kt-kecamatan').prop('disabled', false);
data.forEach(function (kecamatan) {
$('#add-kt-kecamatan').append('<option value="' + kecamatan.id + '">' + kecamatan.nama + '</option>');
});
}
});
}
});
$('#add-kt-kecamatan').select2().on('change', function (e) {
e.preventDefault();
var kecamatanId = $(this).val();
$('#add-kt-desa').prop('disabled', !kecamatanId).empty().append('<option value="">Pilih Desa/Kelurahan</option>').trigger('change');
if (kecamatanId) {
$.ajax({
url: '{{ route("desakelurahan.bykecamatan", ":id") }}'.replace(':id', kecamatanId),
method: 'GET',
success: function (data) {
$('#add-kt-desa').prop('disabled', false);
data.forEach(function (desa) {
$('#add-kt-desa').append('<option value="' + desa.id + '">' + desa.nama + '</option>');
});
}
});
}
});
$('#add-kt-desa').select2();
});
</script>
@endpush
@@ -0,0 +1,230 @@
@extends('layout.main')
@section('content')
<div class="container-xxl flex-grow-1 container-p-y kelompok-tani-page">
@include('produktivitas.survey-kelompok-tani.header', ['kelompokTani' => $kelompokTani])
@php
$kegiatanOptions = [
'Pertemuan/diskusi rutin',
'Pelatihan bersama',
'Pengadaan input bersama (pupuk/bibit)',
'Pengolahan pascapanen bersama',
'Pemasaran/penjualan kolektif',
'Simpan pinjam kelompok',
'Mengumpulkan hasil panen',
];
$tindakanOptions = [
'Difermentasi',
'Disetorkan ke BUMDes/Koperasi',
'Dijual langsung ke tengkulak/Offtaker',
'Dijual untuk pendanaan kelompok',
];
$fungsiOptions = [
'Pendampingan/penguatan praktik budidaya (GAP) di kebun anggota',
'Pengumpulan/agregasi hasil panen anggota',
'Penyalur informasi & program bantuan dari pemerintah/NGO ke anggota',
'Advokasi/perwakilan kepentingan anggota ke pihak luar',
'Belum ada fungsi spesifik yang berjalan',
];
$kegiatanSelected = old('kegiatan_terlaksana', $kelompokTani->kegiatan_terlaksana ?? []);
$kegiatanLainnyaValue = collect($kegiatanSelected)->first(fn ($v) => !in_array($v, $kegiatanOptions));
$kegiatanLainnyaChecked = old('kegiatan_terlaksana_lainnya', $kegiatanLainnyaValue) !== null;
$tindakanSelected = old('tindakan_hasil_panen', $kelompokTani->tindakan_hasil_panen ?? []);
$fungsiSelected = old('fungsi_kelompok_bagi_anggota', $kelompokTani->fungsi_kelompok_bagi_anggota ?? []);
$fungsiLainnyaValue = collect($fungsiSelected)->first(fn ($v) => !in_array($v, $fungsiOptions));
$fungsiLainnyaChecked = old('fungsi_kelompok_lainnya', $fungsiLainnyaValue) !== null;
@endphp
<div class="card mb-6" id="section-aktivitas-fungsi">
<div class="card-body pt-6">
<form novalidate method="POST" action="{{ route('kelompok-tani.update', $kelompokTani->id) }}">
@csrf
@method('PUT')
<input type="hidden" name="section" value="aktivitas-fungsi">
<h5 class="pb-4 border-bottom mb-4">Frekuensi Pertemuan Rutin</h5>
<div class="row">
<div class="col-md-6 mb-4 form-control-validation">
<label class="form-label" for="af-frekuensi-pertemuan">Frekuensi Pertemuan</label>
<select class="form-select @error('frekuensi_pertemuan') is-invalid @enderror" id="af-frekuensi-pertemuan" name="frekuensi_pertemuan">
<option value="">Pilih Frekuensi Pertemuan</option>
@foreach([
'Tidak pernah/sudah tidak aktif',
'Tidak rutin/tidak terjadwal',
'1x per 2-3 bulan',
'1x per bulan',
'Lebih dari 1x per bulan',
] as $option)
<option value="{{ $option }}" {{ old('frekuensi_pertemuan', $kelompokTani->frekuensi_pertemuan) == $option ? 'selected' : '' }}>{{ $option }}</option>
@endforeach
</select>
@error('frekuensi_pertemuan')<div class="error invalid-feedback">{{ $message }}</div>@enderror
</div>
<div class="col-md-6 mb-4 form-control-validation">
<label class="form-label" for="af-tanggal-pertemuan-terakhir">Tanggal Pertemuan Terakhir</label>
<input type="text" class="form-control @error('tanggal_pertemuan_terakhir') is-invalid @enderror" id="af-tanggal-pertemuan-terakhir" name="tanggal_pertemuan_terakhir"
value="{{ old('tanggal_pertemuan_terakhir', $kelompokTani->tanggal_pertemuan_terakhir?->format('d-m-Y')) }}" />
@error('tanggal_pertemuan_terakhir')<div class="error invalid-feedback">{{ $message }}</div>@enderror
</div>
</div>
<h5 class="pb-4 border-bottom mb-4 mt-4">Kegiatan Konkret yang Sudah Berjalan</h5>
<p class="text-muted small kelompok-tani-help">
Kegiatan yang sudah benar-benar dilaksanakan, bukan rencana. Boleh pilih lebih dari satu.
</p>
<div class="row mb-2">
@foreach($kegiatanOptions as $option)
<div class="col-md-4 mb-2">
<div class="form-check">
<input class="form-check-input kegiatan-checkbox" type="checkbox" name="kegiatan_terlaksana[]" value="{{ $option }}"
id="kegiatan-{{ $loop->index }}" {{ in_array($option, $kegiatanSelected) ? 'checked' : '' }}
@if($option === 'Mengumpulkan hasil panen') data-toggle-panen @endif>
<label class="form-check-label" for="kegiatan-{{ $loop->index }}">{{ $option }}</label>
</div>
</div>
@endforeach
<div class="col-md-4 mb-2">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="kegiatan_terlaksana[]" value="Lainnya" id="kegiatan-lainnya-check" {{ $kegiatanLainnyaChecked ? 'checked' : '' }}>
<label class="form-check-label" for="kegiatan-lainnya-check">Lainnya</label>
</div>
</div>
</div>
<div class="row" id="kegiatan-lainnya-wrapper" style="display: none;">
<div class="col-md-6 mb-4 form-control-validation">
<input type="text" class="form-control @error('kegiatan_terlaksana_lainnya') is-invalid @enderror" name="kegiatan_terlaksana_lainnya"
id="kegiatan-lainnya-text" placeholder="Sebutkan kegiatan lainnya"
value="{{ old('kegiatan_terlaksana_lainnya', $kegiatanLainnyaValue) }}" />
@error('kegiatan_terlaksana_lainnya')<div class="error invalid-feedback">{{ $message }}</div>@enderror
</div>
</div>
<div id="panen-wrapper" style="display: none;">
<h6 class="pb-2 mt-3">Detail Hasil Panen</h6>
<div class="row">
<div class="col-md-3 mb-4 form-control-validation">
<label class="form-label" for="af-panen-min">Estimasi Panen Minimal</label>
<input type="number" step="0.01" min="0" class="form-control @error('estimasi_panen_minimal') is-invalid @enderror" id="af-panen-min" name="estimasi_panen_minimal"
value="{{ old('estimasi_panen_minimal', $kelompokTani->estimasi_panen_minimal) }}" />
@error('estimasi_panen_minimal')<div class="error invalid-feedback">{{ $message }}</div>@enderror
</div>
<div class="col-md-3 mb-4 form-control-validation">
<label class="form-label" for="af-panen-max">Estimasi Panen Maksimal</label>
<input type="number" step="0.01" min="0" class="form-control @error('estimasi_panen_maksimal') is-invalid @enderror" id="af-panen-max" name="estimasi_panen_maksimal"
value="{{ old('estimasi_panen_maksimal', $kelompokTani->estimasi_panen_maksimal) }}" />
@error('estimasi_panen_maksimal')<div class="error invalid-feedback">{{ $message }}</div>@enderror
</div>
<div class="col-md-3 mb-4 form-control-validation">
<label class="form-label" for="af-satuan-panen">Satuan</label>
<select class="form-select @error('satuan_hasil_panen') is-invalid @enderror" id="af-satuan-panen" name="satuan_hasil_panen">
<option value="">Pilih Satuan</option>
<option value="kg" {{ old('satuan_hasil_panen', $kelompokTani->satuan_hasil_panen) == 'kg' ? 'selected' : '' }}>Kg</option>
<option value="ton" {{ old('satuan_hasil_panen', $kelompokTani->satuan_hasil_panen) == 'ton' ? 'selected' : '' }}>Ton</option>
</select>
@error('satuan_hasil_panen')<div class="error invalid-feedback">{{ $message }}</div>@enderror
</div>
</div>
<label class="form-label d-block">Hasil Panen Kelompok Biasanya Dilanjutkan untuk Apa?</label>
<div class="row mb-4">
@foreach($tindakanOptions as $option)
<div class="col-md-6 mb-2">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="tindakan_hasil_panen[]" value="{{ $option }}"
id="tindakan-{{ $loop->index }}" {{ in_array($option, $tindakanSelected) ? 'checked' : '' }}>
<label class="form-check-label" for="tindakan-{{ $loop->index }}">{{ $option }}</label>
</div>
</div>
@endforeach
</div>
</div>
<h5 class="pb-4 border-bottom mb-4 mt-4">Peran dan Fungsi Kelompok</h5>
<p class="text-muted small kelompok-tani-help">
Fungsi yang melekat secara struktural pada kelompok ini, terlepas dari seberapa sering kegiatannya berjalan.
</p>
<div class="row mb-2">
@foreach($fungsiOptions as $option)
<div class="col-md-6 mb-2">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="fungsi_kelompok_bagi_anggota[]" value="{{ $option }}"
id="fungsi-{{ $loop->index }}" {{ in_array($option, $fungsiSelected) ? 'checked' : '' }}>
<label class="form-check-label" for="fungsi-{{ $loop->index }}">{{ $option }}</label>
</div>
</div>
@endforeach
<div class="col-md-6 mb-2">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="fungsi_kelompok_bagi_anggota[]" value="Lainnya" id="fungsi-lainnya-check" {{ $fungsiLainnyaChecked ? 'checked' : '' }}>
<label class="form-check-label" for="fungsi-lainnya-check">Lainnya</label>
</div>
</div>
</div>
<div class="row" id="fungsi-lainnya-wrapper" style="display: none;">
<div class="col-md-6 mb-4 form-control-validation">
<input type="text" class="form-control @error('fungsi_kelompok_lainnya') is-invalid @enderror" name="fungsi_kelompok_lainnya"
id="fungsi-lainnya-text" placeholder="Sebutkan fungsi lainnya"
value="{{ old('fungsi_kelompok_lainnya', $fungsiLainnyaValue) }}" />
@error('fungsi_kelompok_lainnya')<div class="error invalid-feedback">{{ $message }}</div>@enderror
</div>
</div>
<div class="mt-4 text-end">
<div class="d-inline-flex flex-wrap justify-content-end gap-2">
<a href="{{ route('kelompok-tani.index') }}" class="btn btn-secondary wave-effect wave-light">
Kembali
</a>
<button type="submit" class="btn btn-success wave-effect wave-light">
<i class="icon-base ti tabler-check me-1_5 icon-sm" aria-hidden="true"></i> Simpan
</button>
</div>
</div>
</form>
</div>
</div>
@include('produktivitas.survey-kelompok-tani.aset-keuangan')
@include('produktivitas.survey-kelompok-tani.bantuan-penilaian')
</div>
@endsection
@push('scripts')
<script>
'use strict';
$(document).ready(function () {
$('#af-frekuensi-pertemuan').select2();
$('#af-tanggal-pertemuan-terakhir').flatpickr({
dateFormat: 'd-m-Y'
});
function toggleLainnya(checkboxSelector, wrapperSelector, textInputSelector) {
var $checkbox = $(checkboxSelector);
var $wrapper = $(wrapperSelector);
function sync() {
if ($checkbox.is(':checked')) {
$wrapper.show();
} else {
$wrapper.hide();
$(textInputSelector).val('');
}
}
sync();
$checkbox.on('change', sync);
}
toggleLainnya('#kegiatan-lainnya-check', '#kegiatan-lainnya-wrapper', '#kegiatan-lainnya-text');
toggleLainnya('#fungsi-lainnya-check', '#fungsi-lainnya-wrapper', '#fungsi-lainnya-text');
function togglePanen() {
var checked = $('[data-toggle-panen]').is(':checked');
$('#panen-wrapper').toggle(checked);
}
togglePanen();
$('[data-toggle-panen]').on('change', togglePanen);
});
</script>
@endpush
@@ -0,0 +1,402 @@
@php
$jenisAsetBaku = \App\Http\Controllers\Web\Produktivitas\AsetKelompokTaniController::JENIS_ASET_BAKU;
$asetTersimpan = $kelompokTani->aset->keyBy('jenis_aset');
$asetLainnyaList = $kelompokTani->aset->filter(fn ($a) => !in_array($a->jenis_aset, $jenisAsetBaku));
$statusPengelolaanOptions = ['Swadaya', 'Hibah', 'Pinjaman', 'Sewa'];
$kondisiOptions = ['Berfungsi', 'Perlu perbaikan/evaluasi', 'Rusak/tidak bisa dipakai', 'Mangkrak'];
$asetLainnyaBaru = old('aset_lainnya') ?: [[]];
$indexKas = array_search('Kas/simpanan kelompok', $jenisAsetBaku, true);
$memilikiKas = (bool) old("aset.$indexKas.status_aset", $asetTersimpan->get('Kas/simpanan kelompok')?->status_aset ?? false);
@endphp
{{-- ASET PRODUKTIF --}}
<div class="card mb-6" id="section-aset-baku">
<div class="card-body pt-6">
<h5 class="pb-4 border-bottom mb-4">Aset Produktif Milik Kelompok</h5>
<p class="text-muted small"><span class="text-danger">*</span> Wajib diisi untuk aset yang dipilih Ada.</p>
<form novalidate id="aset-produktif-form" method="POST" action="{{ route('kelompok-tani.aset-keuangan.update', $kelompokTani->id) }}">
@csrf
@method('PUT')
<div class="table-responsive mb-4">
<table class="table table-bordered align-middle">
<thead>
<tr>
<th style="min-width: 180px;">Jenis Aset</th>
<th style="width: 140px;">Ada/Tidak</th>
<th style="min-width: 180px;">Status Pengelolaan <span class="text-danger" aria-hidden="true">*</span></th>
<th style="min-width: 200px;">Kondisi <span class="text-danger" aria-hidden="true">*</span></th>
</tr>
</thead>
<tbody>
@foreach($jenisAsetBaku as $index => $jenis)
@php
$row = $asetTersimpan->get($jenis);
$ada = (bool) old("aset.$index.status_aset", $row?->status_aset ?? false);
$statusPengelolaan = old("aset.$index.status_pengelolaan_aset", $row?->status_pengelolaan_aset);
$kondisi = old("aset.$index.kondisi_aset", $row?->kondisi_aset);
@endphp
<tr @if($index === $indexKas) data-aset-kas @endif>
<td>
<input type="hidden" name="aset[{{ $index }}][jenis_aset]" value="{{ $jenis }}">
{{ $jenis }}
</td>
<td>
<div class="form-check">
<input class="form-check-input aset-status-radio" type="radio" name="aset[{{ $index }}][status_aset]" value="1" id="aset-{{ $index }}-ada" data-row="{{ $index }}" @checked($ada)>
<label class="form-check-label" for="aset-{{ $index }}-ada">Ada</label>
</div>
<div class="form-check">
<input class="form-check-input aset-status-radio" type="radio" name="aset[{{ $index }}][status_aset]" value="0" id="aset-{{ $index }}-tidak" data-row="{{ $index }}" @checked(!$ada)>
<label class="form-check-label" for="aset-{{ $index }}-tidak">Tidak</label>
</div>
</td>
<td>
<select class="form-select aset-detail-select @error("aset.$index.status_pengelolaan_aset") is-invalid @enderror" id="aset-{{ $index }}-status-pengelolaan" name="aset[{{ $index }}][status_pengelolaan_aset]" aria-label="Status Pengelolaan {{ $jenis }}" @disabled(!$ada) @required($ada)
@error("aset.$index.status_pengelolaan_aset") aria-invalid="true" aria-describedby="aset-{{ $index }}-status-pengelolaan-error" @enderror>
<option value="">Pilih status pengelola</option>
@foreach($statusPengelolaanOptions as $option)
<option value="{{ $option }}" @selected($statusPengelolaan == $option)>{{ $option }}</option>
@endforeach
</select>
<div class="invalid-feedback" id="aset-{{ $index }}-status-pengelolaan-error" role="alert">
@error("aset.$index.status_pengelolaan_aset") {{ $message }} @else Status Pengelolaan wajib diisi jika aset dipilih Ada. @enderror
</div>
</td>
<td>
<select class="form-select aset-detail-select @error("aset.$index.kondisi_aset") is-invalid @enderror" id="aset-{{ $index }}-kondisi" name="aset[{{ $index }}][kondisi_aset]" aria-label="Kondisi {{ $jenis }}" @disabled(!$ada) @required($ada)
@error("aset.$index.kondisi_aset") aria-invalid="true" aria-describedby="aset-{{ $index }}-kondisi-error" @enderror>
<option value="">Pilih kondisi</option>
@foreach($kondisiOptions as $option)
<option value="{{ $option }}" @selected($kondisi == $option)>{{ $option }}</option>
@endforeach
</select>
<div class="invalid-feedback" id="aset-{{ $index }}-kondisi-error" role="alert">
@error("aset.$index.kondisi_aset") {{ $message }} @else Kondisi wajib diisi jika aset dipilih Ada. @enderror
</div>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
<div id="section-aset-lainnya" class="mt-4">
<h6 class="pb-3 border-bottom mb-4">Aset Lainnya</h6>
{{-- DATA LAMA --}}
@if($asetLainnyaList->isNotEmpty())
@foreach($asetLainnyaList as $row)
<div class="row align-items-end border-bottom pb-3 mb-3">
<div class="col-md-4">
<label class="form-label">Jenis Aset</label>
<input type="text" class="form-control" value="{{ $row->jenis_aset }}" disabled>
</div>
<div class="col-md-3">
<label class="form-label">Status Pengelolaan</label>
<input type="text" class="form-control" value="{{ $row->status_pengelolaan_aset }}" disabled>
</div>
<div class="col-md-4">
<label class="form-label">Kondisi</label>
<input type="text" class="form-control" value="{{ $row->kondisi_aset }}" disabled>
</div>
<div class="col-md-1">
<button type="submit" form="delete-aset-{{ $row->id }}" class="btn btn-outline-danger" aria-label="Hapus aset {{ $row->jenis_aset }}">
<i class="icon-base ti tabler-trash icon-sm"></i>
</button>
</div>
</div>
@endforeach
@else
<p class="text-muted mb-4">Belum ada aset lainnya di luar daftar baku.</p>
@endif
{{-- TOMBOL PEMBUKA — form tambah disembunyikan dulu karena aset lainnya sifatnya opsional --}}
@php $bukaFormAsetLainnya = old('aset_lainnya') || $errors->has('aset_lainnya.*'); @endphp
<button type="button" class="btn btn-outline-success" id="btn-toggle-aset-lainnya" style="display: {{ $bukaFormAsetLainnya ? 'none' : 'inline-block' }};">
<i class="icon-base ti tabler-plus me-1_5 icon-sm"></i> Tambahkan Aset Lainnya
</button>
{{-- FORM TAMBAH DATA BARU — tetap kebuka kalau baru gagal validasi, biar isian & errornya kelihatan --}}
<div id="aset-lainnya-form-wrapper" style="display: {{ $bukaFormAsetLainnya ? 'block' : 'none' }};" class="mt-4">
<div class="form-repeater-aset-lainnya">
<div data-repeater-list="aset_lainnya">
@foreach($asetLainnyaBaru as $index => $item)
<div data-repeater-item>
<div class="row align-items-end">
<div class="col-md-4 mb-3">
<label class="form-label">Jenis Aset</label>
<input type="text" class="form-control aset-lainnya-input" name="jenis_aset" placeholder="Contoh: Mesin pengering" value="{{ $item['jenis_aset'] ?? '' }}">
</div>
<div class="col-md-3 mb-3">
<label class="form-label">Status Pengelolaan</label>
<select class="form-select aset-lainnya-select" name="status_pengelolaan_aset">
<option value="">Pilih status pengelola</option>
@foreach($statusPengelolaanOptions as $option)
<option value="{{ $option }}" @selected(($item['status_pengelolaan_aset'] ?? '') == $option)>{{ $option }}</option>
@endforeach
</select>
</div>
<div class="col-md-4 mb-3">
<label class="form-label">Kondisi</label>
<select class="form-select aset-lainnya-select" name="kondisi_aset">
<option value="">Pilih kondisi</option>
@foreach($kondisiOptions as $option)
<option value="{{ $option }}" @selected(($item['kondisi_aset'] ?? '') == $option)>{{ $option }}</option>
@endforeach
</select>
</div>
<div class="col-md-1 mb-3">
<button type="button" class="btn btn-outline-danger btn-delete-aset-lainnya" data-repeater-delete disabled>
<i class="icon-base ti tabler-trash icon-sm"></i>
</button>
</div>
</div>
<hr>
</div>
@endforeach
</div>
<div class="mt-4 d-flex flex-wrap justify-content-between gap-2">
<button type="button" class="btn btn-outline-success" id="btn-tambah-baris-aset-lainnya" data-repeater-create disabled>
<i class="icon-base ti tabler-plus me-1_5 icon-sm"></i> Tambah Baris
</button>
</div>
</div>
</div>
</div>
<div class="mt-4 text-end">
<div class="d-inline-flex flex-wrap justify-content-end gap-2">
<a href="{{ route('kelompok-tani.index') }}" class="btn btn-secondary wave-effect wave-light">
Kembali
</a>
<button type="submit" class="btn btn-success wave-effect wave-light">
<i class="icon-base ti tabler-check me-1_5 icon-sm" aria-hidden="true"></i> Simpan
</button>
</div>
</div>
</form>
@foreach($asetLainnyaList as $row)
<form id="delete-aset-{{ $row->id }}" method="POST" action="{{ route('kelompok-tani.aset-keuangan.destroy', [$kelompokTani->id, $row->id]) }}" class="delete-db-form" hidden>
@csrf
@method('DELETE')
</form>
@endforeach
</div>
</div>
{{-- KAS / SIMPAN-PINJAM --}}
<div class="card mb-6" id="section-kas" @if(!$memilikiKas) hidden @endif>
<div class="card-body pt-6">
<h5 class="pb-4 border-bottom mb-4">Kapasitas Pengelolaan Kas/Simpan-Pinjam Kelompok</h5>
<p class="text-muted small kelompok-tani-help">
Hanya isi bagian ini jika kelompok memiliki kas/simpan-pinjam (lihat baris "Kas/simpanan kelompok" di tabel aset).
</p>
<form novalidate method="POST" action="{{ route('kelompok-tani.update', $kelompokTani->id) }}">
@csrf
@method('PUT')
<input type="hidden" name="section" value="kas">
<div class="row">
<div class="col-md-4 mb-4 form-control-validation">
<label class="form-label" for="ak-pembukuan-kas">Pembukuan/Pencatatan Keuangan Kas</label>
<select class="form-select @error('pembukuan_kas') is-invalid @enderror" id="ak-pembukuan-kas" name="pembukuan_kas">
<option value="">Pilih Status Pembukuan</option>
@foreach([
'Tidak ada pencatatan sama sekali',
'Ada, tapi tidak rutin/tidak rapi',
'Ada, rutin dan rapi',
] as $option)
<option value="{{ $option }}" {{ old('pembukuan_kas', $kelompokTani->pembukuan_kas) == $option ? 'selected' : '' }}>{{ $option }}</option>
@endforeach
</select>
@error('pembukuan_kas')<div class="error invalid-feedback">{{ $message }}</div>@enderror
</div>
<div class="col-md-4 mb-4 form-control-validation">
<label class="form-label" for="ak-pengelola-kas">Siapa yang Mengelola/Memegang Kas</label>
<input type="text" class="form-control @error('pengelola_kas') is-invalid @enderror" id="ak-pengelola-kas" name="pengelola_kas"
value="{{ old('pengelola_kas', $kelompokTani->pengelola_kas) }}" />
@error('pengelola_kas')<div class="error invalid-feedback">{{ $message }}</div>@enderror
</div>
<div class="col-md-4 mb-4 form-control-validation">
<label class="form-label" for="ak-masalah-kas">Pernah Ada Masalah Pengelolaan Kas?</label>
<select class="form-select @error('masalah_pengelolaan_kas_simpan_pinjam') is-invalid @enderror" id="ak-masalah-kas" name="masalah_pengelolaan_kas_simpan_pinjam">
<option value="">Pilih Status</option>
@foreach([
'Tidak pernah ada masalah',
'Pernah, sudah selesai',
'Pernah, masih berlanjut/belum selesai',
] as $option)
<option value="{{ $option }}" {{ old('masalah_pengelolaan_kas_simpan_pinjam', $kelompokTani->masalah_pengelolaan_kas_simpan_pinjam) == $option ? 'selected' : '' }}>{{ $option }}</option>
@endforeach
</select>
@error('masalah_pengelolaan_kas_simpan_pinjam')<div class="error invalid-feedback">{{ $message }}</div>@enderror
</div>
</div>
<div class="row" id="ak-deskripsi-masalah-wrapper" style="display: none;">
<div class="col-md-12 mb-4 form-control-validation">
<label class="form-label" for="ak-deskripsi-masalah">Ceritakan Singkat</label>
<textarea class="form-control @error('deskripsi_masalah_pengelolaan_kas_simpan_pinjam') is-invalid @enderror" id="ak-deskripsi-masalah" name="deskripsi_masalah_pengelolaan_kas_simpan_pinjam" rows="2">{{ old('deskripsi_masalah_pengelolaan_kas_simpan_pinjam', $kelompokTani->deskripsi_masalah_pengelolaan_kas_simpan_pinjam) }}</textarea>
@error('deskripsi_masalah_pengelolaan_kas_simpan_pinjam')<div class="error invalid-feedback">{{ $message }}</div>@enderror
</div>
</div>
<div class="text-end">
<div class="d-inline-flex flex-wrap justify-content-end gap-2">
<a href="{{ route('kelompok-tani.index') }}" class="btn btn-secondary wave-effect wave-light">
Kembali
</a>
<button type="submit" class="btn btn-success wave-effect wave-light">
<i class="icon-base ti tabler-check me-1_5 icon-sm" aria-hidden="true"></i> Simpan
</button>
</div>
</div>
</form>
</div>
</div>
@push('scripts')
<script>
'use strict';
$(document).ready(function () {
var assetForm = document.getElementById('aset-produktif-form');
assetForm.addEventListener('invalid', function (event) {
if (!event.target.matches('.aset-detail-select')) return;
event.preventDefault();
var field = event.target;
$(field).addClass('is-invalid').attr({
'aria-invalid': 'true',
'aria-describedby': field.id + '-error'
});
if (field === assetForm.querySelector('.aset-detail-select:invalid')) {
field.focus();
}
}, true);
$('.aset-detail-select').on('change', function () {
if (this.validity.valid) {
$(this).removeClass('is-invalid').removeAttr('aria-invalid aria-describedby');
}
});
function toggleKasSection() {
var memilikiKas = $('[data-aset-kas] .aset-status-radio[value="1"]').is(':checked');
$('#section-kas').prop('hidden', !memilikiKas);
}
toggleKasSection();
$('[data-aset-kas] .aset-status-radio').on('change', toggleKasSection);
function toggleDeskripsiMasalah() {
var val = $('#ak-masalah-kas').val();
$('#ak-deskripsi-masalah-wrapper').toggle(val === 'Pernah, sudah selesai' || val === 'Pernah, masih berlanjut/belum selesai');
}
toggleDeskripsiMasalah();
$('#ak-masalah-kas').on('change', toggleDeskripsiMasalah);
// Status Pengelolaan & Kondisi cuma relevan kalau asetnya "Ada" — disable & kosongkan
// begitu "Tidak" dipilih, biar tidak ada kesan barang yang tidak ada tapi diisi detailnya.
$('.aset-status-radio').on('change', function () {
var $row = $(this).closest('tr');
var ada = $(this).val() === '1';
$row.find('.aset-detail-select').prop({ disabled: !ada, required: ada });
if (!ada) {
$row.find('.aset-detail-select').val('').removeClass('is-invalid').removeAttr('aria-invalid aria-describedby');
}
});
// Tombol pembuka form "Aset Lainnya" (disembunyikan dulu karena opsional)
$('#btn-toggle-aset-lainnya').on('click', function () {
$('#aset-lainnya-form-wrapper').slideDown();
$(this).hide();
});
// Repeater "Aset Lainnya"
var $form = $('.form-repeater-aset-lainnya');
var $btnTambah = $('#btn-tambah-baris-aset-lainnya');
function getRows() {
return $form.find('[data-repeater-item]');
}
function rowAdaIsi($row) {
return String($row.find('.aset-lainnya-input').val() || '').trim() !== '';
}
function updateButtons() {
var $rows = getRows();
var $lastRow = $rows.last();
$rows.each(function () {
var $row = $(this);
$row.find('.btn-delete-aset-lainnya').prop('disabled', !rowAdaIsi($row));
});
$btnTambah.prop('disabled', !rowAdaIsi($lastRow));
}
$form.repeater({
initEmpty: false,
show: function () {
var $row = $(this);
$row.find('.aset-lainnya-input').val('');
$row.find('.aset-lainnya-select').val('');
$row.find('.btn-delete-aset-lainnya').prop('disabled', true);
$row.slideDown();
setTimeout(updateButtons, 50);
},
hide: function (deleteElement) {
var $row = $(this);
if (!rowAdaIsi($row)) {
return;
}
Swal.fire({
title: 'Hapus baris ini?',
text: 'Baris ini hanya akan dihapus dari form.',
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Ya, hapus',
cancelButtonText: 'Batal',
customClass: { confirmButton: 'btn btn-danger me-3', cancelButton: 'btn btn-label-secondary' },
buttonsStyling: false
}).then(function (result) {
if (result.isConfirmed) {
$row.slideUp(deleteElement);
setTimeout(updateButtons, 300);
}
});
}
});
$(document).on('input keyup change', '.form-repeater-aset-lainnya .aset-lainnya-input', updateButtons);
updateButtons();
$(document).on('submit', '#section-aset-baku .delete-db-form', function (e) {
e.preventDefault();
var form = this;
Swal.fire({
title: 'Hapus aset ini?',
text: 'Data yang sudah dihapus tidak bisa dikembalikan.',
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Ya, hapus',
cancelButtonText: 'Batal',
customClass: { confirmButton: 'btn btn-danger me-3', cancelButton: 'btn btn-label-secondary' },
buttonsStyling: false
}).then(function (result) {
if (result.isConfirmed) {
form.submit();
}
});
});
});
</script>
@endpush
@@ -0,0 +1,228 @@
@php
$pernahBantuan = old('pernah_mendapatkan_bantuan', $kelompokTani->pernah_mendapatkan_bantuan);
$tampilkanBantuan = (bool) $pernahBantuan;
$hasSavedAid = $kelompokTani->bantuan->contains(fn ($bantuan) => filled($bantuan->nama_program));
$requireFirstRow = $tampilkanBantuan && !$hasSavedAid;
$bantuanBaru = old('bantuan') ?: [[]];
$bantuanBaru = is_array($bantuanBaru) ? $bantuanBaru : [[]];
@endphp
<div class="card mb-6" id="section-status-bantuan">
<div class="card-body pt-6">
<h5 class="pb-4 border-bottom mb-4">Pengalaman Akses Bantuan/Program</h5>
<form novalidate id="pengalaman-bantuan-form" method="POST" action="{{ route('kelompok-tani.update', $kelompokTani->id) }}">
@csrf
@method('PUT')
<input type="hidden" name="section" value="status-bantuan">
<div class="row">
<div class="col-md-6 mb-4 form-control-validation">
<label class="form-label d-block">Apakah kelompok ini pernah mendapatkan bantuan atau akses program dari pemerintah/lembaga lain?<span class="text-danger">*</span></label>
<div class="form-check form-check-inline">
<input class="form-check-input @error('pernah_mendapatkan_bantuan') is-invalid @enderror" type="radio" name="pernah_mendapatkan_bantuan" value="1" id="sb-pernah" @checked($tampilkanBantuan) required>
<label class="form-check-label" for="sb-pernah">Pernah</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input @error('pernah_mendapatkan_bantuan') is-invalid @enderror" type="radio" name="pernah_mendapatkan_bantuan" value="0" id="sb-belum" @checked(!is_null($pernahBantuan) && !$tampilkanBantuan) required>
<label class="form-check-label" for="sb-belum">Belum Pernah</label>
</div>
@error('pernah_mendapatkan_bantuan')<div class="error invalid-feedback d-block">{{ $message }}</div>@enderror
</div>
</div>
<div id="section-bantuan" @if(!$tampilkanBantuan) hidden @endif>
<h6 class="pb-3 border-bottom mb-4">Daftar Program/Bantuan yang Pernah Diterima</h6>
<p class="text-muted small mb-4">Minimal satu data bantuan wajib diisi.</p>
@forelse($kelompokTani->bantuan as $row)
<div class="row align-items-end border-bottom pb-3 mb-3">
<div class="col-md-3">
<label class="form-label">Nama Program/Bantuan @if($loop->first)<span class="text-danger bantuan-saved-required-mark" aria-hidden="true">*</span>@endif</label>
<input type="text" class="form-control" value="{{ $row->nama_program }}" disabled>
</div>
<div class="col-md-3">
<label class="form-label">Penyelenggara @if($loop->first)<span class="text-danger bantuan-saved-required-mark" aria-hidden="true">*</span>@endif</label>
<input type="text" class="form-control" value="{{ $row->penyelenggara }}" disabled>
</div>
<div class="col-md-3">
<label class="form-label">Bentuk Bantuan @if($loop->first)<span class="text-danger bantuan-saved-required-mark" aria-hidden="true">*</span>@endif</label>
<input type="text" class="form-control" value="{{ $row->bentuk_bantuan }}" disabled>
</div>
<div class="col-md-2">
<label class="form-label">Tahun @if($loop->first)<span class="text-danger bantuan-saved-required-mark" aria-hidden="true">*</span>@endif</label>
<input type="text" class="form-control" value="{{ $row->tahun }}" disabled>
</div>
<div class="col-md-1">
<button type="submit" form="delete-bantuan-{{ $row->id }}" class="btn btn-outline-danger" aria-label="Hapus bantuan {{ $row->nama_program }}">
<i class="icon-base ti tabler-trash icon-sm"></i>
</button>
</div>
</div>
@empty
<p class="text-muted mb-4">Belum ada data program/bantuan.</p>
@endforelse
<fieldset id="bantuan-fields" @disabled(!$tampilkanBantuan)>
<div class="form-repeater-bantuan" data-has-saved-bantuan="{{ $hasSavedAid ? '1' : '0' }}">
<div data-repeater-list="bantuan">
@foreach($bantuanBaru as $index => $item)
@php
$item = is_array($item) ? $item : [];
$required = $loop->first && $requireFirstRow;
@endphp
<div data-repeater-item>
<div class="row align-items-end">
<div class="col-md-3 mb-3">
<label class="form-label">Nama Program/Bantuan <span class="text-danger bantuan-required-mark" @if(!$required) hidden @endif>*</span></label>
<input type="text" class="form-control bantuan-input @error("bantuan.$index.nama_program") is-invalid @enderror" name="nama_program" placeholder="Contoh: Bantuan Bibit Kakao" value="{{ $item['nama_program'] ?? '' }}" @required($required)>
@error("bantuan.$index.nama_program")<div class="invalid-feedback">{{ $message }}</div>@enderror
</div>
<div class="col-md-3 mb-3">
<label class="form-label">Penyelenggara <span class="text-danger bantuan-required-mark" @if(!$required) hidden @endif>*</span></label>
<input type="text" class="form-control bantuan-input @error("bantuan.$index.penyelenggara") is-invalid @enderror" name="penyelenggara" placeholder="Contoh: Dinas Pertanian" value="{{ $item['penyelenggara'] ?? '' }}" @required($required)>
@error("bantuan.$index.penyelenggara")<div class="invalid-feedback">{{ $message }}</div>@enderror
</div>
<div class="col-md-3 mb-3">
<label class="form-label">Bentuk Bantuan <span class="text-danger bantuan-required-mark" @if(!$required) hidden @endif>*</span></label>
<input type="text" class="form-control bantuan-input @error("bantuan.$index.bentuk_bantuan") is-invalid @enderror" name="bentuk_bantuan" placeholder="Contoh: Bibit, pupuk, pelatihan" value="{{ $item['bentuk_bantuan'] ?? '' }}" @required($required)>
@error("bantuan.$index.bentuk_bantuan")<div class="invalid-feedback">{{ $message }}</div>@enderror
</div>
<div class="col-md-2 mb-3">
<label class="form-label">Tahun <span class="text-danger bantuan-required-mark" @if(!$required) hidden @endif>*</span></label>
<input type="number" class="form-control bantuan-year-input @error("bantuan.$index.tahun") is-invalid @enderror" name="tahun" placeholder="Contoh: 2023" min="1900" max="{{ date('Y') }}" value="{{ $item['tahun'] ?? '' }}" @required($required)>
@error("bantuan.$index.tahun")<div class="invalid-feedback">{{ $message }}</div>@enderror
</div>
<div class="col-md-1 mb-3">
<button type="button" class="btn btn-outline-danger btn-delete-bantuan" data-repeater-delete disabled>
<i class="icon-base ti tabler-trash icon-sm"></i>
</button>
</div>
</div>
<hr>
</div>
@endforeach
</div>
<button type="button" class="btn btn-outline-success" id="btn-tambah-bantuan" data-repeater-create disabled>
<i class="icon-base ti tabler-plus me-1_5 icon-sm"></i> Tambah Baris
</button>
</div>
</fieldset>
</div>
<div class="mt-4 text-end">
<div class="d-inline-flex flex-wrap justify-content-end gap-2">
<a href="{{ route('kelompok-tani.index') }}" class="btn btn-secondary wave-effect wave-light">
Kembali
</a>
<button type="submit" class="btn btn-success wave-effect wave-light">
<i class="icon-base ti tabler-check me-1_5 icon-sm" aria-hidden="true"></i> Simpan
</button>
</div>
</div>
</form>
@foreach($kelompokTani->bantuan as $row)
<form id="delete-bantuan-{{ $row->id }}" method="POST" action="{{ route('kelompok-tani.bantuan-penilaian.destroy', [$kelompokTani->id, $row->id]) }}" class="delete-db-form" hidden>
@csrf
@method('DELETE')
</form>
@endforeach
</div>
</div>
@push('scripts')
<script>
'use strict';
$(function () {
function toggleDaftarBantuan() {
var pernah = $('#sb-pernah').is(':checked');
$('#section-bantuan').prop('hidden', !pernah);
$('#bantuan-fields').prop('disabled', !pernah);
updateButtons();
}
var $repeater = $('.form-repeater-bantuan');
var $btnTambah = $('#btn-tambah-bantuan');
function rowAdaIsi($row) {
return $row.find('.bantuan-input, .bantuan-year-input').toArray().some(function (input) {
return String($(input).val() || '').trim() !== '';
});
}
function rowLengkap($row) {
return $row.find('.bantuan-input, .bantuan-year-input[required]').toArray().every(function (input) {
return String($(input).val() || '').trim() !== '';
});
}
function updateButtons() {
var $rows = $repeater.find('[data-repeater-item]');
var needsFirst = $('#sb-pernah').is(':checked') && $repeater.attr('data-has-saved-bantuan') !== '1';
$rows.each(function (index) {
var required = needsFirst && index === 0;
$(this).find('.bantuan-input, .bantuan-year-input').prop('required', required);
$(this).find('.bantuan-required-mark').prop('hidden', !required);
$(this).find('.btn-delete-bantuan').prop('disabled', $rows.length === 1 && (required || !rowAdaIsi($(this))));
});
$btnTambah.prop('disabled', !rowLengkap($rows.last()));
}
$repeater.repeater({
initEmpty: false,
show: function () {
$(this).find('.bantuan-input, .bantuan-year-input').val('');
$(this).find('.is-invalid').removeClass('is-invalid');
$(this).find('.invalid-feedback').remove();
$(this).slideDown();
updateButtons();
},
hide: function (deleteElement) {
var $row = $(this);
if (!rowAdaIsi($row)) {
deleteElement();
updateButtons();
return;
}
Swal.fire({
title: 'Hapus baris ini?',
text: 'Baris ini hanya akan dihapus dari form.',
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Ya, hapus',
cancelButtonText: 'Batal',
customClass: { confirmButton: 'btn btn-danger me-3', cancelButton: 'btn btn-label-secondary' },
buttonsStyling: false
}).then(function (result) {
if (result.isConfirmed) {
$row.slideUp(function () { deleteElement(); updateButtons(); });
}
});
}
});
$(document).on('input change', '.form-repeater-bantuan .bantuan-input, .form-repeater-bantuan .bantuan-year-input', updateButtons);
toggleDaftarBantuan();
$('#sb-pernah, #sb-belum').on('change', toggleDaftarBantuan);
$(document).on('submit', '#section-status-bantuan .delete-db-form', function (event) {
event.preventDefault();
var form = this;
Swal.fire({
title: 'Hapus data bantuan ini?',
text: 'Data yang sudah dihapus tidak bisa dikembalikan.',
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Ya, hapus',
cancelButtonText: 'Batal',
customClass: { confirmButton: 'btn btn-danger me-3', cancelButton: 'btn btn-label-secondary' },
buttonsStyling: false
}).then(function (result) {
if (result.isConfirmed) form.submit();
});
});
});
</script>
@endpush

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