Compare commits
152
Commits
4a560ebc4e
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
013cb7b728 | ||
|
|
4ab745cfe5 | ||
|
|
c66eed56e2 | ||
|
|
8be0906a2e | ||
|
|
fcd79b7cfe | ||
|
|
bea9be8a39 | ||
|
|
5220b10de7 | ||
|
|
24725e3df1 | ||
|
|
60d32a88e9 | ||
|
|
4370d3c3d0 | ||
|
|
7699430301 | ||
|
|
df2b5a1312 | ||
|
|
2e8271d27a | ||
|
|
9b5c0a7309 | ||
|
|
9d56b67998 | ||
|
|
f95f43999a | ||
|
|
3e172cf915 | ||
|
|
3a9b3dbd7b | ||
|
|
80f0056efe | ||
|
|
aef27704f2 | ||
|
|
f49bb0c2db | ||
|
|
8a2c0292ac | ||
|
|
9ea324e553 | ||
|
|
94c3288ea7 | ||
|
|
8539bdcec4 | ||
|
|
77de648ded | ||
|
|
393cf0d0d1 | ||
|
|
7fdf9b400d | ||
|
|
be4d46004e | ||
|
|
fef5358402 | ||
|
|
02eed6da34 | ||
|
|
94d3f2a582 | ||
|
|
471ca5ac0e | ||
|
|
8ec8f0c9d6 | ||
|
|
4d7d16e9f6 | ||
|
|
1155b9b95b | ||
|
|
607533a327 | ||
|
|
6b25bff2c4 | ||
|
|
71b4d42ea2 | ||
|
|
c42a2571f5 | ||
|
|
16903b21e3 | ||
|
|
1d4a44b8ac | ||
|
|
539239b507 | ||
|
|
a76b014056 | ||
|
|
d50c292cf7 | ||
|
|
dacaec7a86 | ||
|
|
6967eafe98 | ||
|
|
118cda625a | ||
|
|
a3f7cb4669 | ||
|
|
ec009a2c55 | ||
|
|
b63dafdaee | ||
|
|
b97f682cd3 | ||
|
|
404019bab6 | ||
|
|
609919ba30 | ||
|
|
047cb4a779 | ||
|
|
530c297b53 | ||
|
|
8fbf68e6ef | ||
|
|
ac8b370248 | ||
|
|
88762f7afb | ||
|
|
b96de514e3 | ||
|
|
6830142a82 | ||
|
|
4b26e82afb | ||
|
|
b855a857b2 | ||
|
|
ba7520a23c | ||
|
|
0b333ab4e9 | ||
|
|
3b1f15d5b3 | ||
|
|
605506bba8 | ||
|
|
c960ba14b1 | ||
|
|
a4f38998d5 | ||
|
|
0d9f6c0b64 | ||
|
|
81775b0c13 | ||
|
|
ba1a318d09 | ||
|
|
0761d64254 | ||
|
|
67c3c6c809 | ||
|
|
04387268cd | ||
|
|
946fabb9a5 | ||
|
|
0a0c86d648 | ||
|
|
8b21fab8b8 | ||
|
|
16a0a3c56c | ||
|
|
d4c6b64ba6 | ||
|
|
b4089d72b3 | ||
|
|
01990ccec1 | ||
|
|
8aed88a31f | ||
|
|
4aa0780802 | ||
|
|
4e41246382 | ||
|
|
43fe9e5f7f | ||
|
|
c8d1994d3b | ||
|
|
cf0f4b47b4 | ||
|
|
24a8016710 | ||
|
|
20e1eee6ff | ||
|
|
c0432cd5f8 | ||
|
|
0ce0936a80 | ||
|
|
502c932f21 | ||
|
|
e60a66eeb7 | ||
|
|
4475cd0098 | ||
|
|
67c111a2b3 | ||
|
|
3bcaa3ea50 | ||
|
|
bed9a6f620 | ||
|
|
712b10e405 | ||
|
|
6cfcf46ba6 | ||
|
|
b1b9828514 | ||
|
|
7af7e1bdaf | ||
|
|
bd05fc7f0f | ||
|
|
17b5687db8 | ||
|
|
e7f48b6500 | ||
|
|
a5884d0f8c | ||
|
|
9576c6295e | ||
|
|
079e300d5d | ||
|
|
2c94316439 | ||
|
|
690b53e5a2 | ||
|
|
50c9adfdc7 | ||
|
|
696c7dede8 | ||
|
|
5967e7d6f3 | ||
|
|
4c66d72dc5 | ||
|
|
c43483a9f8 | ||
|
|
e02a34a527 | ||
|
|
713355ce0a | ||
|
|
12f8d0bbda | ||
|
|
8afe7f9b38 | ||
|
|
fbf84eb4cb | ||
|
|
e04093ad3f | ||
|
|
3e1d3a33ee | ||
|
|
e316d064ef | ||
|
|
0c289ec244 | ||
|
|
089f1b515d | ||
|
|
4c647ea8ce | ||
|
|
419404e273 | ||
|
|
837e542851 | ||
|
|
9378d15d71 | ||
|
|
0381e9bf0e | ||
|
|
3a70445e9a | ||
|
|
d2ed2c58fd | ||
|
|
9b201efa06 | ||
|
|
2b3a0b7c58 | ||
|
|
a187fb1095 | ||
|
|
6b44c11181 | ||
|
|
090153e53a | ||
|
|
a79a93d291 | ||
|
|
62f0f1bdbc | ||
|
|
f0acd8c7ed | ||
|
|
3908b0be71 | ||
|
|
05cc3be9d3 | ||
|
|
f4c83d348a | ||
|
|
61bd8dc388 | ||
|
|
002d6044b4 | ||
|
|
30196278d7 | ||
|
|
08c19c0714 | ||
|
|
d6c036e19a | ||
|
|
981bc0d494 | ||
|
|
857da1e900 | ||
|
|
66fc7227e6 | ||
|
|
3edfc57ba7 |
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums\AksesPasar;
|
||||
|
||||
enum TingkatKepercayaanPembeli: int
|
||||
{
|
||||
case SANGAT_PERCAYA = 1;
|
||||
case CUKUP_PERCAYA = 2;
|
||||
case RAGU_RAGU = 3;
|
||||
case TIDAK_PERCAYA = 4;
|
||||
|
||||
public static function options(): array
|
||||
{
|
||||
return [
|
||||
['value' => self::SANGAT_PERCAYA->value, 'label' => 'Sangat percaya, berani langsung coba'],
|
||||
['value' => self::CUKUP_PERCAYA->value, 'label' => 'Cukup percaya, tapi hati hati'],
|
||||
['value' => self::RAGU_RAGU->value, 'label' => 'Ragu ragu'],
|
||||
['value' => self::TIDAK_PERCAYA->value, 'label' => 'Tidak percaya, enggan mencoba'],
|
||||
];
|
||||
}
|
||||
|
||||
public static function values(): array
|
||||
{
|
||||
return array_column(self::cases(), 'value');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums\Bumdes;
|
||||
|
||||
enum JabatanBumdes: string
|
||||
{
|
||||
case DIREKTUR = 'direktur';
|
||||
case DIREKTUR_KETUA = 'direktur_ketua';
|
||||
case SEKRETARIS = 'sekretaris';
|
||||
case BENDAHARA = 'bendahara';
|
||||
case KEPALA_UNIT_USAHA = 'kepala_unit_usaha';
|
||||
case PENGAWAS = 'pengawas';
|
||||
case PENASIHAT = 'penasihat';
|
||||
case ANGGOTA = 'anggota';
|
||||
case STAF_OPERASIONAL = 'staf_operasional';
|
||||
case LAINNYA = 'lainnya';
|
||||
|
||||
public function label(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::DIREKTUR => 'Direktur/Ketua',
|
||||
self::DIREKTUR_KETUA => 'Direktur/Ketua',
|
||||
self::SEKRETARIS => 'Sekretaris',
|
||||
self::BENDAHARA => 'Bendahara',
|
||||
self::KEPALA_UNIT_USAHA => 'Kepala Unit Usaha',
|
||||
self::PENGAWAS => 'Pengawas',
|
||||
self::PENASIHAT => 'Penasihat',
|
||||
self::ANGGOTA => 'Anggota',
|
||||
self::STAF_OPERASIONAL => 'Staf Operasional',
|
||||
self::LAINNYA => 'Lainnya',
|
||||
};
|
||||
}
|
||||
|
||||
public static function options(): array
|
||||
{
|
||||
return collect([
|
||||
self::DIREKTUR_KETUA,
|
||||
self::SEKRETARIS,
|
||||
self::BENDAHARA,
|
||||
self::LAINNYA,
|
||||
])->map(fn ($case) => [
|
||||
'value' => $case->value,
|
||||
'label' => $case->label(),
|
||||
])->toArray();
|
||||
}
|
||||
}
|
||||
@@ -11,9 +11,9 @@ enum KeparahanSerangan: string
|
||||
public function label(): string
|
||||
{
|
||||
return match($this) {
|
||||
self::RINGAN => 'Ringan',
|
||||
self::SEDANG => 'Sedang',
|
||||
self::BERAT => 'Berat',
|
||||
self::RINGAN => 'Ringan, hanya beberapa tanaman',
|
||||
self::SEDANG => 'Sedang, sebagian tanaman terdampak',
|
||||
self::BERAT => 'Parah, sebagian besar tanaman terdampak',
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -13,10 +13,10 @@ enum KondisiTanaman: string
|
||||
public function label(): string
|
||||
{
|
||||
return match($this) {
|
||||
self::BAIK => 'Baik',
|
||||
self::SEDANG => 'Sedang',
|
||||
self::BURUK => 'Buruk',
|
||||
self::MATI => 'Mati',
|
||||
self::BAIK => 'Baik, tumbuh lebat, banyak buah',
|
||||
self::SEDANG => 'Sedang, cukup tumbuh, ada masalah',
|
||||
self::BURUK => 'Buruk, banyak tanaman tua dan lemah/tidak berbuah',
|
||||
self::MATI => 'Mati, tanaman telah mati, tidak dapat tumbuh maupun berbuah',
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums\Koperasi;
|
||||
|
||||
enum PosisiKoperasi: string
|
||||
{
|
||||
case PENGURUS = 'Pengurus koperasi';
|
||||
case ANGGOTA = 'Anggota koperasi';
|
||||
case PENDAMPING = 'Pendamping koperasi';
|
||||
}
|
||||
@@ -19,13 +19,13 @@ enum Pendidikan: string
|
||||
{
|
||||
return match($this) {
|
||||
self::TIDAK_SEKOLAH => 'Tidak Sekolah',
|
||||
self::SD => 'SD atau Sederajat',
|
||||
self::SMP => 'SMP atau Sederajat',
|
||||
self::SMA => 'SMA atau Sederajat',
|
||||
self::SD => 'SD',
|
||||
self::SMP => 'SMP',
|
||||
self::SMA => 'SMA/SMK',
|
||||
self::DIPLOMA => 'Diploma 1-3 (D1-D3)',
|
||||
self::SARJANA => 'Sarjana (S1) atau sederajat',
|
||||
self::MAGISTER => 'Magister (S2) atau sederajat',
|
||||
self::DOKTOR => 'Doktor (S3) atau sederajat',
|
||||
self::SARJANA => 'Sarjana (S1)',
|
||||
self::MAGISTER => 'Magister (S2)',
|
||||
self::DOKTOR => 'Doktor (S3)',
|
||||
self::PESANTREN => 'Pesantren, Semiari, Wihara, atau Pendidikan Keagamaan Formal',
|
||||
self::LAINNYA => 'Lainnya',
|
||||
};
|
||||
|
||||
@@ -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)',
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -97,11 +97,12 @@ class LahanController extends Controller
|
||||
'nama' => ['required', 'string', 'max:255'],
|
||||
'status_kepemilikan' => ['required', new Enum(StatusLahan::class)],
|
||||
'desa_kelurahan_id' => ['nullable', 'numeric', 'exists:master_desa_kelurahan,id'],
|
||||
'path' => ['nullable', 'array'],
|
||||
'path.*' => ['array'],
|
||||
'path' => ['nullable', 'string'],
|
||||
'luas_lahan' => ['nullable', 'numeric'],
|
||||
'uid' => ['required', 'string'],
|
||||
'profile_id' => $profileRule,
|
||||
'foto' => ['nullable', 'array'],
|
||||
'foto.*' => ['image', 'mimes:jpeg,png,jpg', 'max:5120'], // Max 5MB
|
||||
]);
|
||||
|
||||
$targetProfileId = $user->role === 'fasilitator'
|
||||
@@ -120,10 +121,22 @@ class LahanController extends Controller
|
||||
? $profile->desa_kelurahan_id
|
||||
: ($validated['desa_kelurahan_id'] ?? $profile->desa_kelurahan_id);
|
||||
|
||||
$fotoPaths = [];
|
||||
if ($request->hasFile('foto')) {
|
||||
foreach ($request->file('foto') as $file) {
|
||||
$fotoPaths[] = $file->store('foto_lahan', 'public');
|
||||
}
|
||||
}
|
||||
|
||||
$lahan = Lahan::create([
|
||||
...$validated,
|
||||
'profile_id' => $profile->id,
|
||||
'nama' => $validated['nama'],
|
||||
'status_kepemilikan' => $validated['status_kepemilikan'],
|
||||
'desa_kelurahan_id' => $desaKelurahanId,
|
||||
'path' => $validated['path'] ?? null,
|
||||
'luas_lahan' => $validated['luas_lahan'] ?? null,
|
||||
'uid' => $validated['uid'],
|
||||
'profile_id' => $profile->id,
|
||||
'foto' => $fotoPaths,
|
||||
]);
|
||||
|
||||
return (new LahanResource($lahan))
|
||||
@@ -153,7 +166,7 @@ class LahanController extends Controller
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, string $id): JsonResponse
|
||||
public function update(Request $request, string $id): JsonResponse
|
||||
{
|
||||
if (!Gate::any(['petani', 'fasilitator','super_admin'])) {
|
||||
abort(403);
|
||||
@@ -173,9 +186,26 @@ class LahanController extends Controller
|
||||
'path' => ['nullable', 'array'],
|
||||
'path.*' => ['array'],
|
||||
'luas_lahan' => ['nullable', 'numeric'],
|
||||
'foto' => ['nullable', 'array'],
|
||||
'foto.*' => ['image', 'mimes:jpeg,png,jpg', 'max:5120'],
|
||||
]);
|
||||
|
||||
$lahan->update($validated);
|
||||
$fotoPaths = $lahan->foto ?? [];
|
||||
|
||||
if ($request->hasFile('foto')) {
|
||||
foreach ($request->file('foto') as $file) {
|
||||
$fotoPaths[] = $file->store('foto_lahan', 'public');
|
||||
}
|
||||
}
|
||||
|
||||
$lahan->update([
|
||||
'nama' => $validated['nama'],
|
||||
'status_kepemilikan' => $validated['status_kepemilikan'],
|
||||
'desa_kelurahan_id' => $validated['desa_kelurahan_id'] ?? $lahan->desa_kelurahan_id,
|
||||
'path' => $validated['path'] ?? null,
|
||||
'luas_lahan' => $validated['luas_lahan'] ?? null,
|
||||
'foto' => $fotoPaths,
|
||||
]);
|
||||
|
||||
return (new LahanResource($lahan))->response();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,580 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Web\Bumdes;
|
||||
|
||||
use App\Enums\Bumdes\JabatanBumdes;
|
||||
use App\Enums\Pendidikan;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Bumdes\KelembagaanAktorBumdes;
|
||||
use App\Models\Bumdes\PengurusBumdes;
|
||||
use App\Models\Wilayah\Provinsi;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class PengurusBumdesController extends Controller
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
if (! Gate::any(['super_admin', 'admin', 'fasilitator'])) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$query = PengurusBumdes::with([
|
||||
...$this->wilayahRelationsWithoutProgramScope(),
|
||||
'creator:id,name',
|
||||
])
|
||||
->orderBy('created_at', 'desc');
|
||||
|
||||
if ($request->search) {
|
||||
$query->where(function ($query) use ($request) {
|
||||
$query->where('nama', 'like', '%' . $request->search . '%')
|
||||
->orWhere('jabatan', 'like', '%' . $request->search . '%')
|
||||
->orWhere('jabatan_lainnya', 'like', '%' . $request->search . '%');
|
||||
});
|
||||
}
|
||||
|
||||
if (auth()->user()->role->value === 'fasilitator') {
|
||||
$query->where('created_by', auth()->id());
|
||||
}
|
||||
|
||||
$summaryQuery = clone $query;
|
||||
$totalPengurus = (clone $summaryQuery)->count();
|
||||
$totalLakiLaki = (clone $summaryQuery)->where('jenis_kelamin', true)->count();
|
||||
$totalPerempuan = (clone $summaryQuery)->where('jenis_kelamin', false)->count();
|
||||
$totalDesaKelurahan = (clone $summaryQuery)->distinct('desa_kelurahan_id')->count('desa_kelurahan_id');
|
||||
$totalJabatan = (clone $summaryQuery)->distinct('jabatan')->count('jabatan');
|
||||
|
||||
$pengurus = $query->paginate($request->per_page ?? 10);
|
||||
$userRole = auth()->user()->role->value;
|
||||
$showEnumerator = in_array($userRole, ['super_admin', 'admin']);
|
||||
|
||||
return view('bumdes.profil.index', compact(
|
||||
'pengurus',
|
||||
'totalPengurus',
|
||||
'totalLakiLaki',
|
||||
'totalPerempuan',
|
||||
'totalDesaKelurahan',
|
||||
'totalJabatan',
|
||||
'showEnumerator'
|
||||
));
|
||||
}
|
||||
|
||||
public function create()
|
||||
{
|
||||
if (! Gate::any(['super_admin', 'admin', 'fasilitator'])) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$provinsi = $this->provinsiOptions();
|
||||
$jabatan = JabatanBumdes::options();
|
||||
$pendidikan = Pendidikan::options();
|
||||
|
||||
return view('bumdes.profil.add', compact('provinsi', 'jabatan', 'pendidikan'));
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
if (! Gate::any(['super_admin', 'admin', 'fasilitator'])) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$validatedData = $this->validatedData($request);
|
||||
|
||||
$pengurus = PengurusBumdes::create([
|
||||
...$this->formatData($validatedData),
|
||||
'uid' => 'BMD-' . strtoupper(Str::random(10)),
|
||||
'created_by' => auth()->id(),
|
||||
'updated_by' => auth()->id(),
|
||||
]);
|
||||
|
||||
return redirect()->route('bumdes.edit', $pengurus->id)->with('success', 'Data pengurus BUMDes berhasil ditambahkan.');
|
||||
}
|
||||
|
||||
public function show(string $id)
|
||||
{
|
||||
return redirect()->route('bumdes.edit', $id);
|
||||
}
|
||||
|
||||
public function edit(string $id)
|
||||
{
|
||||
if (! Gate::any(['super_admin', 'admin', 'fasilitator'])) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$pengurus = $this->pengurusQuery()
|
||||
->with($this->wilayahRelationsWithoutProgramScope())
|
||||
->findOrFail($id);
|
||||
$provinsi = $this->provinsiOptions();
|
||||
$jabatan = JabatanBumdes::options();
|
||||
$pendidikan = Pendidikan::options();
|
||||
|
||||
return view('bumdes.profil.edit', compact('pengurus', 'provinsi', 'jabatan', 'pendidikan'));
|
||||
}
|
||||
|
||||
public function update(Request $request, string $id)
|
||||
{
|
||||
if (! Gate::any(['super_admin', 'admin', 'fasilitator'])) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$pengurus = $this->pengurusQuery()->findOrFail($id);
|
||||
$validatedData = $this->validatedData($request);
|
||||
|
||||
$pengurus->update([
|
||||
...$this->formatData($validatedData),
|
||||
'updated_by' => auth()->id(),
|
||||
]);
|
||||
|
||||
return redirect()->route('bumdes.edit', $pengurus->id)->with('success', 'Data pengurus BUMDes berhasil diupdate.');
|
||||
}
|
||||
|
||||
public function destroy(string $id)
|
||||
{
|
||||
if (! Gate::any(['super_admin', 'admin', 'fasilitator'])) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$pengurus = $this->pengurusQuery()->findOrFail($id);
|
||||
$pengurus->update(['deleted_by' => auth()->id()]);
|
||||
$pengurus->delete();
|
||||
|
||||
return response()->json(['message' => 'Data pengurus BUMDes berhasil dihapus.']);
|
||||
}
|
||||
|
||||
public function kelembagaanAktor(string $id)
|
||||
{
|
||||
if (! Gate::any(['super_admin', 'admin', 'fasilitator'])) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$pengurus = $this->pengurusQuery()
|
||||
->with([
|
||||
...$this->wilayahRelationsWithoutProgramScope(),
|
||||
'kelembagaanAktor',
|
||||
])
|
||||
->findOrFail($id);
|
||||
$kelembagaanAktor = $pengurus->kelembagaanAktor;
|
||||
|
||||
return view('bumdes.kelembagaan-aktor.index', array_merge(
|
||||
compact('pengurus', 'kelembagaanAktor'),
|
||||
$this->kelembagaanAktorOptions()
|
||||
));
|
||||
}
|
||||
|
||||
public function storeKelembagaanAktor(Request $request, string $id)
|
||||
{
|
||||
if (! Gate::any(['super_admin', 'admin', 'fasilitator'])) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$pengurus = $this->pengurusQuery()->findOrFail($id);
|
||||
$validatedData = $this->validatedKelembagaanAktorData($request);
|
||||
|
||||
KelembagaanAktorBumdes::updateOrCreate(
|
||||
['pengurus_bumdes_id' => $pengurus->id],
|
||||
[
|
||||
...$this->formatKelembagaanAktorData($validatedData),
|
||||
'created_by' => $pengurus->kelembagaanAktor?->created_by ?? auth()->id(),
|
||||
'updated_by' => auth()->id(),
|
||||
]
|
||||
);
|
||||
|
||||
return redirect()
|
||||
->route('bumdes.kelembagaan-aktor.index', $pengurus->id)
|
||||
->with('success', 'Data kelembagaan dan aktor BUMDes berhasil disimpan.');
|
||||
}
|
||||
|
||||
private function pengurusQuery()
|
||||
{
|
||||
$query = PengurusBumdes::query();
|
||||
|
||||
if (auth()->user()->role->value === 'fasilitator') {
|
||||
$query->where('created_by', auth()->id());
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
private function provinsiOptions()
|
||||
{
|
||||
return Provinsi::withoutGlobalScope('program')
|
||||
->select('id', 'kode', 'nama')
|
||||
->orderBy('kode', 'asc')
|
||||
->get();
|
||||
}
|
||||
|
||||
private function wilayahRelationsWithoutProgramScope(): array
|
||||
{
|
||||
return [
|
||||
'desaKelurahan' => fn ($query) => $query->withoutGlobalScope('program')->select('id', 'nama', 'kecamatan_id'),
|
||||
'desaKelurahan.kecamatan' => fn ($query) => $query->withoutGlobalScope('program')->select('id', 'nama', 'kabupaten_kota_id'),
|
||||
'desaKelurahan.kecamatan.kabupatenKota' => fn ($query) => $query->withoutGlobalScope('program')->select('id', 'nama', 'provinsi_id'),
|
||||
'desaKelurahan.kecamatan.kabupatenKota.provinsi' => fn ($query) => $query->withoutGlobalScope('program')->select('id', 'nama'),
|
||||
];
|
||||
}
|
||||
|
||||
private function validatedData(Request $request): array
|
||||
{
|
||||
return $request->validate([
|
||||
'nama' => ['required', 'string', 'max:255'],
|
||||
'jenis_kelamin' => ['required', 'boolean'],
|
||||
'tempat_lahir' => ['required', 'string', 'max:255'],
|
||||
'tanggal_lahir' => ['required', 'date_format:d-m-Y'],
|
||||
'pendidikan_terakhir' => ['required', Rule::enum(Pendidikan::class)],
|
||||
'alamat' => ['required', 'string', 'max:500'],
|
||||
'desa_kelurahan_id' => ['required', 'exists:master_desa_kelurahan,id'],
|
||||
'nomor_telepon' => ['nullable', 'digits_between:11,13'],
|
||||
'jabatan' => ['required', Rule::enum(JabatanBumdes::class)],
|
||||
'jabatan_lainnya' => ['nullable', 'required_if:jabatan,' . JabatanBumdes::LAINNYA->value, 'string', 'max:255'],
|
||||
'lama_menjabat_tahun' => ['required', 'integer', 'min:0', 'max:80'],
|
||||
], [
|
||||
'nama.required' => 'Nama harus diisi.',
|
||||
'jenis_kelamin.required' => 'Jenis kelamin harus dipilih.',
|
||||
'tempat_lahir.required' => 'Tempat lahir harus diisi.',
|
||||
'tanggal_lahir.required' => 'Tanggal lahir harus diisi.',
|
||||
'tanggal_lahir.date_format' => 'Tanggal lahir harus menggunakan format tanggal yang valid.',
|
||||
'pendidikan_terakhir.required' => 'Pendidikan terakhir harus dipilih.',
|
||||
'alamat.required' => 'Alamat harus diisi.',
|
||||
'desa_kelurahan_id.required' => 'Desa kelurahan harus dipilih.',
|
||||
'desa_kelurahan_id.exists' => 'Desa kelurahan tidak ditemukan.',
|
||||
'nomor_telepon.digits_between' => 'Nomor telepon harus berupa angka 11 sampai 13 digit.',
|
||||
'jabatan.required' => 'Jabatan/peran harus dipilih.',
|
||||
'jabatan_lainnya.required_if' => 'Jabatan lainnya harus diisi.',
|
||||
'lama_menjabat_tahun.required' => 'Lama menjabat harus diisi.',
|
||||
'lama_menjabat_tahun.integer' => 'Lama menjabat harus berupa angka tahun.',
|
||||
]);
|
||||
}
|
||||
|
||||
private function formatData(array $data): array
|
||||
{
|
||||
$data['tanggal_lahir'] = Carbon::createFromFormat('d-m-Y', $data['tanggal_lahir'])->format('Y-m-d');
|
||||
$data['tanggal_mulai_menjabat'] = Carbon::now()
|
||||
->subYears((int) $data['lama_menjabat_tahun'])
|
||||
->format('Y-m-d');
|
||||
|
||||
if (($data['jabatan'] ?? null) !== JabatanBumdes::LAINNYA->value) {
|
||||
$data['jabatan_lainnya'] = null;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
private function validatedKelembagaanAktorData(Request $request): array
|
||||
{
|
||||
$this->normalizeKelembagaanAktorInput($request);
|
||||
$maxYear = now()->year + 1;
|
||||
|
||||
return $request->validate([
|
||||
'status_badan_hukum' => ['nullable', Rule::in(array_keys($this->statusBadanHukumOptions()))],
|
||||
'nomor_dasar_hukum' => ['nullable', 'string', 'max:255'],
|
||||
'tahun_dasar_hukum' => ['nullable', 'integer', 'digits:4', 'min:1900', 'max:' . $maxYear],
|
||||
'status_keaktifan' => ['nullable', Rule::in(array_keys($this->statusKeaktifanOptions()))],
|
||||
'alasan_tidak_aktif' => ['nullable', 'string'],
|
||||
'unit_usaha' => ['nullable', 'array'],
|
||||
'unit_usaha.*' => ['string', Rule::in(array_keys($this->unitUsahaOptions()))],
|
||||
'unit_usaha_jasa_lainnya' => ['nullable', 'string', 'max:255'],
|
||||
'status_unit_komoditas' => ['nullable', Rule::in(array_keys($this->statusUnitKomoditasOptions()))],
|
||||
'nama_unit_komoditas' => ['nullable', 'string', 'max:255'],
|
||||
'status_ad_art' => ['nullable', Rule::in(array_keys($this->statusAdArtOptions()))],
|
||||
'tahun_ad_art' => ['nullable', 'integer', 'digits:4', 'min:1900', 'max:' . $maxYear],
|
||||
'pengurus_inti' => ['nullable', 'array'],
|
||||
'pengurus_inti.*.jabatan' => ['nullable', 'string', 'max:255'],
|
||||
'pengurus_inti.*.nama' => ['nullable', 'string', 'max:255'],
|
||||
'pengurus_inti.*.aktif' => ['nullable', 'boolean'],
|
||||
'memiliki_staf' => ['nullable', 'boolean'],
|
||||
'jumlah_staf' => ['nullable', 'integer', 'min:0'],
|
||||
'unit_usaha_staf' => ['nullable', 'string'],
|
||||
'modal_awal' => ['nullable', 'integer', 'min:0'],
|
||||
'tahun_modal_awal' => ['nullable', 'integer', 'digits:4', 'min:1900', 'max:' . $maxYear],
|
||||
'pernah_penambahan_modal' => ['nullable', 'boolean'],
|
||||
'penambahan_modal' => ['nullable', 'array'],
|
||||
'penambahan_modal.*.tahun' => ['nullable', 'integer', 'digits:4', 'min:1900', 'max:' . $maxYear],
|
||||
'penambahan_modal.*.jumlah' => ['nullable', 'integer', 'min:0'],
|
||||
'aset_produktif' => ['nullable', 'array'],
|
||||
'aset_produktif.*.aset' => ['nullable', 'string', 'max:255'],
|
||||
'aset_produktif.*.dipilih' => ['nullable', 'boolean'],
|
||||
'aset_produktif.*.jumlah' => ['nullable', 'string', 'max:255'],
|
||||
'aset_produktif.*.kondisi' => ['nullable', 'string', 'max:255'],
|
||||
'aset_produktif_lainnya' => ['nullable', 'string', 'max:255'],
|
||||
'status_kepemilikan_aset' => ['nullable', Rule::in(array_keys($this->statusKepemilikanAsetOptions()))],
|
||||
'infrastruktur_pascapanen' => ['nullable', 'array'],
|
||||
'infrastruktur_pascapanen.*.infrastruktur' => ['nullable', 'string', 'max:255'],
|
||||
'infrastruktur_pascapanen.*.dipilih' => ['nullable', 'boolean'],
|
||||
'infrastruktur_pascapanen.*.jumlah' => ['nullable', 'string', 'max:255'],
|
||||
'infrastruktur_pascapanen.*.kondisi' => ['nullable', Rule::in(array_keys($this->kondisiInfrastrukturOptions()))],
|
||||
'infrastruktur_pascapanen_lainnya' => ['nullable', 'string', 'max:255'],
|
||||
], $this->kelembagaanAktorValidationMessages(), $this->kelembagaanAktorValidationAttributes());
|
||||
}
|
||||
|
||||
private function normalizeKelembagaanAktorInput(Request $request): void
|
||||
{
|
||||
foreach (['modal_awal'] as $field) {
|
||||
if ($request->has($field)) {
|
||||
$request->merge([$field => preg_replace('/\D/', '', (string) $request->input($field))]);
|
||||
}
|
||||
}
|
||||
|
||||
$penambahanModal = collect($request->input('penambahan_modal', []))
|
||||
->map(function ($row) {
|
||||
if (isset($row['jumlah'])) {
|
||||
$row['jumlah'] = preg_replace('/\D/', '', (string) $row['jumlah']);
|
||||
}
|
||||
|
||||
return $row;
|
||||
})
|
||||
->all();
|
||||
|
||||
if ($request->has('penambahan_modal')) {
|
||||
$request->merge(['penambahan_modal' => $penambahanModal]);
|
||||
}
|
||||
}
|
||||
|
||||
private function formatKelembagaanAktorData(array $data): array
|
||||
{
|
||||
$payload = array_merge([
|
||||
'status_badan_hukum' => null,
|
||||
'nomor_dasar_hukum' => null,
|
||||
'tahun_dasar_hukum' => null,
|
||||
'status_keaktifan' => null,
|
||||
'alasan_tidak_aktif' => null,
|
||||
'unit_usaha' => [],
|
||||
'unit_usaha_jasa_lainnya' => null,
|
||||
'status_unit_komoditas' => null,
|
||||
'nama_unit_komoditas' => null,
|
||||
'status_ad_art' => null,
|
||||
'tahun_ad_art' => null,
|
||||
'pengurus_inti' => [],
|
||||
'memiliki_staf' => null,
|
||||
'jumlah_staf' => null,
|
||||
'unit_usaha_staf' => null,
|
||||
'modal_awal' => null,
|
||||
'tahun_modal_awal' => null,
|
||||
'pernah_penambahan_modal' => null,
|
||||
'penambahan_modal' => [],
|
||||
'aset_produktif' => [],
|
||||
'aset_produktif_lainnya' => null,
|
||||
'status_kepemilikan_aset' => null,
|
||||
'infrastruktur_pascapanen' => [],
|
||||
'infrastruktur_pascapanen_lainnya' => null,
|
||||
], $data);
|
||||
|
||||
$payload['unit_usaha'] = array_values($payload['unit_usaha'] ?? []);
|
||||
$payload['pengurus_inti'] = $this->filterRows($payload['pengurus_inti'] ?? [], ['nama', 'aktif']);
|
||||
$payload['penambahan_modal'] = $this->filterRows($payload['penambahan_modal'] ?? [], ['tahun', 'jumlah']);
|
||||
$payload['aset_produktif'] = $this->filterRows($payload['aset_produktif'] ?? [], ['dipilih', 'jumlah', 'kondisi']);
|
||||
$payload['infrastruktur_pascapanen'] = $this->filterRows($payload['infrastruktur_pascapanen'] ?? [], ['dipilih', 'jumlah', 'kondisi']);
|
||||
|
||||
if (($payload['status_keaktifan'] ?? null) !== 'tidak_aktif_vakum') {
|
||||
$payload['alasan_tidak_aktif'] = null;
|
||||
}
|
||||
|
||||
if (! in_array('jasa_lainnya', $payload['unit_usaha'], true)) {
|
||||
$payload['unit_usaha_jasa_lainnya'] = null;
|
||||
}
|
||||
|
||||
if (($payload['status_unit_komoditas'] ?? null) !== 'ya') {
|
||||
$payload['nama_unit_komoditas'] = null;
|
||||
}
|
||||
|
||||
if (($payload['status_ad_art'] ?? null) !== 'ada_lengkap') {
|
||||
$payload['tahun_ad_art'] = null;
|
||||
}
|
||||
|
||||
if (! (bool) ($payload['memiliki_staf'] ?? false)) {
|
||||
$payload['jumlah_staf'] = null;
|
||||
$payload['unit_usaha_staf'] = null;
|
||||
}
|
||||
|
||||
if (! (bool) ($payload['pernah_penambahan_modal'] ?? false)) {
|
||||
$payload['penambahan_modal'] = null;
|
||||
}
|
||||
|
||||
if (! collect($payload['aset_produktif'] ?? [])->contains(fn ($row) => ($row['aset'] ?? null) === 'lainnya')) {
|
||||
$payload['aset_produktif_lainnya'] = null;
|
||||
}
|
||||
|
||||
if (! collect($payload['infrastruktur_pascapanen'] ?? [])->contains(fn ($row) => ($row['infrastruktur'] ?? null) === 'lainnya')) {
|
||||
$payload['infrastruktur_pascapanen_lainnya'] = null;
|
||||
}
|
||||
|
||||
return $payload;
|
||||
}
|
||||
|
||||
private function filterRows(array $rows, array $valueKeys): array
|
||||
{
|
||||
return collect($rows)
|
||||
->map(function ($row) {
|
||||
return collect($row)
|
||||
->map(fn ($value) => $value === '' ? null : $value)
|
||||
->all();
|
||||
})
|
||||
->filter(function ($row) use ($valueKeys) {
|
||||
foreach ($valueKeys as $key) {
|
||||
if (filled($row[$key] ?? null)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
})
|
||||
->values()
|
||||
->all();
|
||||
}
|
||||
|
||||
private function kelembagaanAktorOptions(): array
|
||||
{
|
||||
return [
|
||||
'statusBadanHukumOptions' => $this->statusBadanHukumOptions(),
|
||||
'statusKeaktifanOptions' => $this->statusKeaktifanOptions(),
|
||||
'unitUsahaOptions' => $this->unitUsahaOptions(),
|
||||
'statusUnitKomoditasOptions' => $this->statusUnitKomoditasOptions(),
|
||||
'statusAdArtOptions' => $this->statusAdArtOptions(),
|
||||
'pengurusIntiRows' => $this->pengurusIntiRows(),
|
||||
'asetProduktifRows' => $this->asetProduktifRows(),
|
||||
'statusKepemilikanAsetOptions' => $this->statusKepemilikanAsetOptions(),
|
||||
'infrastrukturPascapanenRows' => $this->infrastrukturPascapanenRows(),
|
||||
'kondisiInfrastrukturOptions' => $this->kondisiInfrastrukturOptions(),
|
||||
];
|
||||
}
|
||||
|
||||
private function statusBadanHukumOptions(): array
|
||||
{
|
||||
return [
|
||||
'sudah_berbadan_hukum' => 'Sudah berbadan hukum (terdaftar Kemenkumham)',
|
||||
'proses_pendaftaran' => 'Proses pendaftaran',
|
||||
'belum_berbadan_hukum' => 'Belum berbadan hukum',
|
||||
'tidak_tahu' => 'Tidak tahu',
|
||||
];
|
||||
}
|
||||
|
||||
private function statusKeaktifanOptions(): array
|
||||
{
|
||||
return [
|
||||
'aktif_penuh' => 'Aktif beroperasi penuh',
|
||||
'aktif_terbatas' => 'Aktif tapi terbatas (sebagian unit usaha berjalan)',
|
||||
'tidak_aktif_vakum' => 'Tidak aktif/vakum',
|
||||
'berdiri_belum_beroperasi' => 'Sudah berdiri tapi belum pernah beroperasi',
|
||||
];
|
||||
}
|
||||
|
||||
private function unitUsahaOptions(): array
|
||||
{
|
||||
return [
|
||||
'simpan_pinjam' => 'Simpan pinjam',
|
||||
'perdagangan_toko_desa' => 'Perdagangan/toko desa',
|
||||
'pengelolaan_air_bersih' => 'Pengelolaan air bersih',
|
||||
'wisata_desa_agrowisata' => 'Wisata desa/agrowisata',
|
||||
'pengelolaan_hasil_pertanian_komoditas' => 'Pengelolaan hasil pertanian/komoditas',
|
||||
'penyewaan_alat_aset' => 'Penyewaan alat/aset',
|
||||
'jasa_lainnya' => 'Jasa lainnya',
|
||||
];
|
||||
}
|
||||
|
||||
private function statusUnitKomoditasOptions(): array
|
||||
{
|
||||
return [
|
||||
'ya' => 'Ya, ada unit khusus',
|
||||
'ditangani_pengurus_inti' => 'Tidak ada unit khusus, ditangani langsung pengurus inti',
|
||||
'direncanakan' => 'Belum ada, masih direncanakan',
|
||||
];
|
||||
}
|
||||
|
||||
private function statusAdArtOptions(): array
|
||||
{
|
||||
return [
|
||||
'ada_lengkap' => 'Ada, lengkap',
|
||||
'ada_tidak_lengkap' => 'Ada tapi tidak lengkap',
|
||||
'tidak_ada' => 'Tidak ada',
|
||||
'tidak_tahu' => 'Tidak tahu',
|
||||
];
|
||||
}
|
||||
|
||||
private function pengurusIntiRows(): array
|
||||
{
|
||||
return [
|
||||
'penasihat_kepala_desa' => 'Penasihat (Kepala Desa)',
|
||||
'ketua_pengawas' => 'Ketua Pengawas',
|
||||
'direktur_ketua_bumdes' => 'Direktur/Ketua BUMDes',
|
||||
'sekretaris' => 'Sekretaris',
|
||||
'bendahara' => 'Bendahara',
|
||||
'kepala_unit_usaha_komoditas' => 'Kepala Unit Usaha Komoditas',
|
||||
'lainnya' => 'Lainnya',
|
||||
];
|
||||
}
|
||||
|
||||
private function asetProduktifRows(): array
|
||||
{
|
||||
return [
|
||||
'bangunan_kantor' => 'Bangunan / kantor',
|
||||
'gudang_penyimpanan' => 'Gudang penyimpanan',
|
||||
'kendaraan_operasional_angkut' => 'Kendaraan operasional/angkut',
|
||||
'lahan_usaha' => 'Lahan usaha',
|
||||
'alat_pengolahan_komoditas' => 'Alat pengolahan komoditas',
|
||||
'lainnya' => 'Lainnya',
|
||||
];
|
||||
}
|
||||
|
||||
private function statusKepemilikanAsetOptions(): array
|
||||
{
|
||||
return [
|
||||
'milik_bumdes_sendiri' => 'Milik BUMDes sendiri',
|
||||
'milik_desa_dipinjamkan_dikelola' => 'Milik desa (dipinjamkan/dikelola)',
|
||||
'sewa_pinjam_pihak_lain' => 'Sewa/pinjam pihak lain',
|
||||
];
|
||||
}
|
||||
|
||||
private function infrastrukturPascapanenRows(): array
|
||||
{
|
||||
return [
|
||||
'gudang_penyimpanan_hasil_panen' => 'Gudang penyimpanan hasil panen',
|
||||
'alat_mesin_pengering' => 'Alat/mesin pengering',
|
||||
'mesin_pulper_huller' => 'Mesin pulper/huller',
|
||||
'timbangan' => 'Timbangan',
|
||||
'alat_sortasi' => 'Alat sortasi',
|
||||
'lainnya' => 'Lainnya',
|
||||
];
|
||||
}
|
||||
|
||||
private function kondisiInfrastrukturOptions(): array
|
||||
{
|
||||
return [
|
||||
'baik_berfungsi' => 'Baik dan berfungsi',
|
||||
'rusak_tidak_terpakai' => 'Rusak/tidak terpakai',
|
||||
];
|
||||
}
|
||||
|
||||
private function kelembagaanAktorValidationMessages(): array
|
||||
{
|
||||
return [
|
||||
'integer' => ':attribute harus berupa angka.',
|
||||
'digits' => ':attribute harus terdiri dari :digits digit.',
|
||||
'min' => ':attribute tidak boleh kurang dari :min.',
|
||||
'max' => ':attribute tidak valid.',
|
||||
'in' => ':attribute tidak valid.',
|
||||
'array' => ':attribute harus berupa daftar data.',
|
||||
'boolean' => ':attribute harus dipilih.',
|
||||
];
|
||||
}
|
||||
|
||||
private function kelembagaanAktorValidationAttributes(): array
|
||||
{
|
||||
return [
|
||||
'status_badan_hukum' => 'Status badan hukum',
|
||||
'nomor_dasar_hukum' => 'Nomor dasar hukum',
|
||||
'tahun_dasar_hukum' => 'Tahun dasar hukum',
|
||||
'status_keaktifan' => 'Status keaktifan',
|
||||
'unit_usaha' => 'Unit usaha',
|
||||
'status_unit_komoditas' => 'Unit usaha komoditas',
|
||||
'status_ad_art' => 'Dokumen AD/ART',
|
||||
'memiliki_staf' => 'Status staf/karyawan',
|
||||
'jumlah_staf' => 'Jumlah staf',
|
||||
'modal_awal' => 'Modal awal',
|
||||
'tahun_modal_awal' => 'Tahun modal awal',
|
||||
'pernah_penambahan_modal' => 'Status penambahan modal',
|
||||
'status_kepemilikan_aset' => 'Status kepemilikan aset',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,509 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Web\Dashboard;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
use App\Models\Pertanian\Petani;
|
||||
use App\Models\Pertanian\Komoditas;
|
||||
use App\Models\Pertanian\Lahan;
|
||||
use App\Models\Pertanian\PendapatanKomoditas;
|
||||
use App\Models\Pertanian\SosialEkonomi;
|
||||
|
||||
class DemografiPetaniController extends Controller
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
return redirect()->route('dashboard.karakteristik-petani', array_merge($request->query(), [
|
||||
'tab' => 'demografi-petani',
|
||||
]));
|
||||
}
|
||||
|
||||
public function karakteristik(Request $request)
|
||||
{
|
||||
if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$activeTab = $request->query('tab') === 'indeks-petani'
|
||||
? 'indeks-petani'
|
||||
: 'demografi-petani';
|
||||
|
||||
$demografiData = $this->contentData($request);
|
||||
$indeksData = app(IndeksPetaniController::class)->contentData($request);
|
||||
|
||||
$tableFilters = [
|
||||
'gender' => ['Gender', ['1' => 'Laki-laki', '0' => 'Perempuan']],
|
||||
'umur' => ['Kelompok Umur', ['kurang_30' => '< 30 tahun', '30_39' => '30–39 tahun', '40_49' => '40–49 tahun', '50_59' => '50–59 tahun', '60_keatas' => '≥ 60 tahun']],
|
||||
'pendidikan' => ['Pendidikan', ['tidak_sekolah' => 'Tidak Sekolah', 'sd' => 'SD', 'smp' => 'SMP', 'sma' => 'SMA', 'diploma' => 'Diploma', 's1_keatas' => 'Sarjana ke atas']],
|
||||
'jumlah_tanggungan' => ['Jumlah Tanggungan', ['0' => '0 orang', '1_2' => '1–2 orang', '3_4' => '3–4 orang', '5_keatas' => '≥ 5 orang']],
|
||||
'kelompok_tani' => ['Keanggotaan Kelompok Tani', ['terdaftar' => 'Terdaftar', 'tidak_terdaftar' => 'Tidak Terdaftar', 'belum_terisi' => 'Belum Terisi']],
|
||||
'komoditas' => ['Komoditas', $indeksData['komoditas']->pluck('nama', 'id')->all()],
|
||||
'luas_lahan' => ['Luas Lahan', ['kurang_05' => '< 0,5 ha', '05_1' => '0,5–1 ha', '1_2' => '> 1–2 ha', 'lebih_2' => '> 2 ha']],
|
||||
'status_kepemilikan_lahan' => ['Status Kepemilikan Lahan', ['sendiri' => 'Milik Pribadi', 'sewa' => 'Sewa', 'adat' => 'Tanah Adat']],
|
||||
'penghasilan' => ['Rentang Pendapatan', ['kurang_1' => '< Rp1 juta', '1_3' => 'Rp1–3 juta', '3_5' => '> Rp3–5 juta', '5_10' => '> Rp5–10 juta', 'lebih_10' => '> Rp10 juta']],
|
||||
];
|
||||
|
||||
return view('dashboard.karakteristik-petani', [
|
||||
'activeTab' => $activeTab,
|
||||
'tableFilters' => $tableFilters,
|
||||
'dashboardFilters' => $request->only([
|
||||
'provinsi_id',
|
||||
'kabupatenKota_id',
|
||||
'kecamatan_id',
|
||||
'desaKelurahan_id',
|
||||
'komoditas_id',
|
||||
'segmen',
|
||||
]),
|
||||
'indeksFilterAction' => route('dashboard.karakteristik-petani'),
|
||||
] + $demografiData + $indeksData);
|
||||
}
|
||||
|
||||
public function contentData(Request $request): array
|
||||
{
|
||||
$filteredPetaniIds = $this->filteredPetaniIdsQuery($request);
|
||||
|
||||
$petaniCounts = Petani::whereIn('petani.id', clone $filteredPetaniIds)->selectRaw("
|
||||
COUNT(*) as total,
|
||||
COUNT(*) FILTER (WHERE jenis_kelamin = true) as laki,
|
||||
COUNT(*) FILTER (WHERE jenis_kelamin = false) as perempuan
|
||||
")->first();
|
||||
|
||||
$totalPetani = $petaniCounts->total;
|
||||
$petaniLakilaki = $petaniCounts->laki;
|
||||
$petaniPerempuan = $petaniCounts->perempuan;
|
||||
|
||||
$totalKomoditas = Komoditas::query()
|
||||
->where('is_utama', true)
|
||||
->whereHas('petani', fn ($query) => $query->whereIn('petani.id', clone $filteredPetaniIds))
|
||||
->count();
|
||||
|
||||
$totalLuasLahan = Lahan::has('petani')
|
||||
->whereIn('petani_id', clone $filteredPetaniIds)
|
||||
->sum('luas_lahan');
|
||||
$ratarataLuasLahan = $totalLuasLahan && $totalPetani ? $totalLuasLahan / $totalPetani : 0;
|
||||
|
||||
$umurCounts = Petani::whereIn('petani.id', clone $filteredPetaniIds)
|
||||
->whereNotNull('tanggal_lahir')->selectRaw("
|
||||
COUNT(*) FILTER (WHERE EXTRACT(YEAR FROM AGE(CURRENT_DATE, tanggal_lahir)) < 30) as kurang_30,
|
||||
COUNT(*) FILTER (WHERE EXTRACT(YEAR FROM AGE(CURRENT_DATE, tanggal_lahir)) BETWEEN 30 AND 39) as umur_30_39,
|
||||
COUNT(*) FILTER (WHERE EXTRACT(YEAR FROM AGE(CURRENT_DATE, tanggal_lahir)) BETWEEN 40 AND 49) as umur_40_49,
|
||||
COUNT(*) FILTER (WHERE EXTRACT(YEAR FROM AGE(CURRENT_DATE, tanggal_lahir)) BETWEEN 50 AND 59) as umur_50_59,
|
||||
COUNT(*) FILTER (WHERE EXTRACT(YEAR FROM AGE(CURRENT_DATE, tanggal_lahir)) >= 60) as umur_60_keatas
|
||||
")->first();
|
||||
|
||||
$pendidikanCounts = Petani::whereIn('petani.id', clone $filteredPetaniIds)
|
||||
->whereNotNull('pendidikan_terakhir')
|
||||
->select('pendidikan_terakhir', DB::raw('COUNT(*) as total'))
|
||||
->groupBy('pendidikan_terakhir')
|
||||
->pluck('total', 'pendidikan_terakhir');
|
||||
|
||||
$pendidikan = [
|
||||
'tidak_sekolah' => (int) ($pendidikanCounts['01_tidak_sekolah'] ?? 0),
|
||||
'sd' => (int) ($pendidikanCounts['02_sd'] ?? 0),
|
||||
'smp' => (int) ($pendidikanCounts['03_smp'] ?? 0),
|
||||
'sma' => (int) ($pendidikanCounts['04_sma'] ?? 0),
|
||||
'diploma' => (int) ($pendidikanCounts['05_diploma'] ?? 0),
|
||||
's1_keatas' => (int) ($pendidikanCounts['06_sarjana'] ?? 0) + (int) ($pendidikanCounts['07_magister'] ?? 0),
|
||||
];
|
||||
|
||||
$jumlahTanggunganBuckets = SosialEkonomi::query()
|
||||
->whereIn('petani_id', clone $filteredPetaniIds)
|
||||
->whereNotNull('jumlah_tanggungan')
|
||||
->selectRaw("
|
||||
COUNT(DISTINCT petani_id) FILTER (WHERE jumlah_tanggungan = 0) as tanggungan_0,
|
||||
COUNT(DISTINCT petani_id) FILTER (WHERE jumlah_tanggungan BETWEEN 1 AND 2) as tanggungan_1_2,
|
||||
COUNT(DISTINCT petani_id) FILTER (WHERE jumlah_tanggungan BETWEEN 3 AND 4) as tanggungan_3_4,
|
||||
COUNT(DISTINCT petani_id) FILTER (WHERE jumlah_tanggungan >= 5) as tanggungan_5_keatas
|
||||
")
|
||||
->first();
|
||||
|
||||
$jumlahTanggungan = [
|
||||
'0' => (int) ($jumlahTanggunganBuckets->tanggungan_0 ?? 0),
|
||||
'1_2' => (int) ($jumlahTanggunganBuckets->tanggungan_1_2 ?? 0),
|
||||
'3_4' => (int) ($jumlahTanggunganBuckets->tanggungan_3_4 ?? 0),
|
||||
'5_keatas' => (int) ($jumlahTanggunganBuckets->tanggungan_5_keatas ?? 0),
|
||||
];
|
||||
|
||||
$kelompokTaniBuckets = Petani::query()
|
||||
->whereIn('petani.id', clone $filteredPetaniIds)
|
||||
->leftJoin('sosial_ekonomi', function ($join) {
|
||||
$join->on('sosial_ekonomi.petani_id', '=', 'petani.id')
|
||||
->whereNull('sosial_ekonomi.deleted_at');
|
||||
})
|
||||
->selectRaw("
|
||||
COUNT(DISTINCT petani.id) FILTER (WHERE sosial_ekonomi.terdaftar_kelompok_tani = 'ya') as terdaftar,
|
||||
COUNT(DISTINCT petani.id) FILTER (WHERE sosial_ekonomi.terdaftar_kelompok_tani = 'tidak') as tidak_terdaftar,
|
||||
COUNT(DISTINCT petani.id) FILTER (WHERE sosial_ekonomi.terdaftar_kelompok_tani IS NULL OR sosial_ekonomi.terdaftar_kelompok_tani NOT IN ('ya', 'tidak')) as belum_terisi
|
||||
")
|
||||
->first();
|
||||
|
||||
$kelompokTani = [
|
||||
'terdaftar' => (int) ($kelompokTaniBuckets->terdaftar ?? 0),
|
||||
'tidak_terdaftar' => (int) ($kelompokTaniBuckets->tidak_terdaftar ?? 0),
|
||||
'belum_terisi' => (int) ($kelompokTaniBuckets->belum_terisi ?? 0),
|
||||
];
|
||||
|
||||
$desaSasaranRows = Petani::query()
|
||||
->leftJoin('master_desa_kelurahan as desa', 'petani.desa_kelurahan_id', '=', 'desa.id')
|
||||
->whereIn('petani.id', clone $filteredPetaniIds)
|
||||
->selectRaw('petani.desa_kelurahan_id as desa_id')
|
||||
->selectRaw("COALESCE(desa.nama, 'Tidak diketahui') as desa")
|
||||
->selectRaw('COUNT(*) as total')
|
||||
->groupBy('petani.desa_kelurahan_id', 'desa.nama')
|
||||
->orderByDesc('total')
|
||||
->orderBy('desa')
|
||||
->get();
|
||||
|
||||
$komoditasUtama = Komoditas::query()
|
||||
->whereHas('petani', fn ($query) => $query->whereIn('petani.id', clone $filteredPetaniIds))
|
||||
->withCount(['petani' => fn ($query) => $query->whereIn('petani.id', clone $filteredPetaniIds)])
|
||||
->orderByDesc('petani_count')
|
||||
->get(['id', 'nama']);
|
||||
|
||||
$luasLahanBuckets = DB::query()->fromSub(
|
||||
Lahan::query()
|
||||
->whereIn('petani_id', clone $filteredPetaniIds)
|
||||
->select('petani_id')
|
||||
->selectRaw('luas_lahan'),
|
||||
'agg'
|
||||
)->selectRaw("
|
||||
COUNT(DISTINCT petani_id) FILTER (WHERE luas_lahan < 0.5) as kurang_05,
|
||||
COUNT(DISTINCT petani_id) FILTER (WHERE luas_lahan >= 0.5 AND luas_lahan <= 1) as luas_05_1,
|
||||
COUNT(DISTINCT petani_id) FILTER (WHERE luas_lahan > 1 AND luas_lahan <= 2) as luas_1_2,
|
||||
COUNT(DISTINCT petani_id) FILTER (WHERE luas_lahan > 2) as lebih_2
|
||||
")->first();
|
||||
|
||||
$luasLahan = [
|
||||
'kurang_05' => $luasLahanBuckets->kurang_05,
|
||||
'05_1' => $luasLahanBuckets->luas_05_1,
|
||||
'1_2' => $luasLahanBuckets->luas_1_2,
|
||||
'lebih_2' => $luasLahanBuckets->lebih_2,
|
||||
];
|
||||
|
||||
$statusKepemilikanLahanRows = Lahan::query()
|
||||
->whereIn('petani_id', clone $filteredPetaniIds)
|
||||
->whereNotNull('status_kepemilikan')
|
||||
->selectRaw('status_kepemilikan as status')
|
||||
->selectRaw('COUNT(DISTINCT petani_id) as total')
|
||||
->groupBy('status_kepemilikan')
|
||||
->orderByDesc('total')
|
||||
->get();
|
||||
|
||||
$pendapatanPerPetaniQuery = PendapatanKomoditas::query()
|
||||
->join('sosial_ekonomi', 'sosial_ekonomi.id', '=', 'pendapatan_komoditas.sosial_ekonomi_id')
|
||||
->whereNotNull('sosial_ekonomi.petani_id')
|
||||
->whereIn('sosial_ekonomi.petani_id', clone $filteredPetaniIds)
|
||||
->groupBy('sosial_ekonomi.petani_id')
|
||||
->selectRaw('SUM(pendapatan_komoditas.pendapatan) as total_pendapatan');
|
||||
|
||||
$penghasilanBuckets = DB::query()->fromSub(
|
||||
clone $pendapatanPerPetaniQuery,
|
||||
'agg'
|
||||
)->selectRaw("
|
||||
AVG(total_pendapatan) as rata_rata,
|
||||
COUNT(*) FILTER (WHERE total_pendapatan < 1000000) as kurang_1,
|
||||
COUNT(*) FILTER (WHERE total_pendapatan >= 1000000 AND total_pendapatan <= 3000000) as p1_3,
|
||||
COUNT(*) FILTER (WHERE total_pendapatan > 3000000 AND total_pendapatan <= 5000000) as p3_5,
|
||||
COUNT(*) FILTER (WHERE total_pendapatan > 5000000 AND total_pendapatan <= 10000000) as p5_10,
|
||||
COUNT(*) FILTER (WHERE total_pendapatan > 10000000) as lebih_10
|
||||
")->first();
|
||||
|
||||
$ratarataPenghasilan = (float) ($penghasilanBuckets->rata_rata ?? 0);
|
||||
|
||||
$penghasilan = [
|
||||
'kurang_1' => $penghasilanBuckets->kurang_1,
|
||||
'1_3' => $penghasilanBuckets->p1_3,
|
||||
'3_5' => $penghasilanBuckets->p3_5,
|
||||
'5_10' => $penghasilanBuckets->p5_10,
|
||||
'lebih_10' => $penghasilanBuckets->lebih_10,
|
||||
];
|
||||
|
||||
$data = [
|
||||
'total_petani' => $totalPetani,
|
||||
'petani_lakilaki' => $petaniLakilaki,
|
||||
'petani_perempuan' => $petaniPerempuan,
|
||||
'total_komoditas' => $totalKomoditas,
|
||||
'total_luas_lahan' => $totalLuasLahan,
|
||||
'ratarata_luas_lahan' => $ratarataLuasLahan,
|
||||
'ratarata_penghasilan' => $ratarataPenghasilan,
|
||||
'kelompok_umur' => [
|
||||
'kurang_30' => $umurCounts->kurang_30,
|
||||
'30_39' => $umurCounts->umur_30_39,
|
||||
'40_49' => $umurCounts->umur_40_49,
|
||||
'50_59' => $umurCounts->umur_50_59,
|
||||
'60_keatas' => $umurCounts->umur_60_keatas,
|
||||
],
|
||||
'pendidikan' => $pendidikan,
|
||||
'jumlah_tanggungan' => $jumlahTanggungan,
|
||||
'kelompok_tani' => $kelompokTani,
|
||||
'desa_sasaran' => [
|
||||
'total' => $desaSasaranRows->count(),
|
||||
'ids' => $desaSasaranRows->pluck('desa_id')->values(),
|
||||
'labels' => $desaSasaranRows->pluck('desa')->values(),
|
||||
'series' => $desaSasaranRows->pluck('total')->map(fn ($v) => (int) $v)->values(),
|
||||
],
|
||||
'komoditas_utama' => [
|
||||
'ids' => $komoditasUtama->pluck('id')->values(),
|
||||
'labels' => $komoditasUtama->pluck('nama')->values(),
|
||||
'series' => $komoditasUtama->pluck('petani_count')->map(fn ($v) => (int) $v)->values(),
|
||||
],
|
||||
'luas_lahan' => $luasLahan,
|
||||
'status_kepemilikan_lahan' => [
|
||||
'keys' => $statusKepemilikanLahanRows->pluck('status')->values(),
|
||||
'labels' => $statusKepemilikanLahanRows
|
||||
->pluck('status')
|
||||
->map(fn ($status) => $this->statusLahanLabel((string) $status))
|
||||
->values(),
|
||||
'series' => $statusKepemilikanLahanRows->pluck('total')->map(fn ($v) => (int) $v)->values(),
|
||||
],
|
||||
'penghasilan' => $penghasilan,
|
||||
];
|
||||
|
||||
return compact('data');
|
||||
}
|
||||
|
||||
private function filteredPetaniIdsQuery(Request $request)
|
||||
{
|
||||
$query = Petani::query()
|
||||
->leftJoin('master_desa_kelurahan as desa', 'petani.desa_kelurahan_id', '=', 'desa.id')
|
||||
->leftJoin('master_kecamatan as kecamatan', 'desa.kecamatan_id', '=', 'kecamatan.id')
|
||||
->leftJoin('master_kabupaten_kota as kabupaten', 'kecamatan.kabupaten_kota_id', '=', 'kabupaten.id')
|
||||
->when($request->filled('provinsi_id'), fn ($query) => $query->where('kabupaten.provinsi_id', $request->provinsi_id))
|
||||
->when($request->filled('kabupatenKota_id'), fn ($query) => $query->where('kecamatan.kabupaten_kota_id', $request->kabupatenKota_id))
|
||||
->when($request->filled('kecamatan_id'), fn ($query) => $query->where('desa.kecamatan_id', $request->kecamatan_id))
|
||||
->when($request->filled('desaKelurahan_id'), fn ($query) => $query->where('petani.desa_kelurahan_id', $request->desaKelurahan_id))
|
||||
->when($request->filled('komoditas_id'), fn ($query) => $query->where('petani.komoditas_id', $request->komoditas_id));
|
||||
|
||||
if ($request->filled('segmen')) {
|
||||
$query->whereHas('skor', function ($query) use ($request) {
|
||||
match ($request->segmen) {
|
||||
'Penggerak' => $query->where('skor_kesiapan_adopsi', '>=', 4),
|
||||
'Perintis' => $query->where('skor_kesiapan_adopsi', '>=', 3)
|
||||
->where('skor_kesiapan_adopsi', '<', 4),
|
||||
'Pemerhati' => $query->where('skor_kesiapan_adopsi', '>=', 2)
|
||||
->where('skor_kesiapan_adopsi', '<', 3),
|
||||
'Pemula' => $query->where('skor_kesiapan_adopsi', '>=', 1)
|
||||
->where('skor_kesiapan_adopsi', '<', 2),
|
||||
'Belum Lengkap' => $query->where(function ($query) {
|
||||
$query->whereNull('skor_kesiapan_adopsi')
|
||||
->orWhere('skor_kesiapan_adopsi', '<', 1);
|
||||
}),
|
||||
default => null,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
return $query->select('petani.id');
|
||||
}
|
||||
|
||||
private function statusLahanLabel(string $status): string
|
||||
{
|
||||
return match ($status) {
|
||||
'sendiri' => 'Milik Pribadi',
|
||||
'sewa' => 'Sewa',
|
||||
'adat' => 'Tanah Adat',
|
||||
default => ucwords(str_replace('_', ' ', $status)),
|
||||
};
|
||||
}
|
||||
|
||||
public function data(Request $request)
|
||||
{
|
||||
$filteredPetaniIds = $this->filteredPetaniIdsQuery($request);
|
||||
$pendapatanSub = PendapatanKomoditas::query()
|
||||
->join('sosial_ekonomi', 'sosial_ekonomi.id', '=', 'pendapatan_komoditas.sosial_ekonomi_id')
|
||||
->whereColumn('sosial_ekonomi.petani_id', 'petani.id')
|
||||
->selectRaw('SUM(pendapatan_komoditas.pendapatan)');
|
||||
|
||||
$query = Petani::with('komoditas')
|
||||
->whereIn('petani.id', clone $filteredPetaniIds)
|
||||
->withSum('lahans as total_luas_lahan', 'luas_lahan')
|
||||
->addSelect(['total_pendapatan' => $pendapatanSub]);
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$query->where('nama', 'ILIKE', "%{$request->search}%");
|
||||
}
|
||||
|
||||
if ($request->has('gender') && $request->gender !== '') {
|
||||
$query->where('jenis_kelamin', $request->gender);
|
||||
}
|
||||
|
||||
if ($request->filled('umur')) {
|
||||
match ($request->umur) {
|
||||
'kurang_30' => $query->whereNotNull('tanggal_lahir')
|
||||
->whereRaw("EXTRACT(YEAR FROM AGE(CURRENT_DATE, tanggal_lahir)) < 30"),
|
||||
'30_39' => $query->whereNotNull('tanggal_lahir')
|
||||
->whereRaw("EXTRACT(YEAR FROM AGE(CURRENT_DATE, tanggal_lahir)) BETWEEN 30 AND 39"),
|
||||
'40_49' => $query->whereNotNull('tanggal_lahir')
|
||||
->whereRaw("EXTRACT(YEAR FROM AGE(CURRENT_DATE, tanggal_lahir)) BETWEEN 40 AND 49"),
|
||||
'50_59' => $query->whereNotNull('tanggal_lahir')
|
||||
->whereRaw("EXTRACT(YEAR FROM AGE(CURRENT_DATE, tanggal_lahir)) BETWEEN 50 AND 59"),
|
||||
'60_keatas' => $query->whereNotNull('tanggal_lahir')
|
||||
->whereRaw("EXTRACT(YEAR FROM AGE(CURRENT_DATE, tanggal_lahir)) >= 60"),
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
|
||||
if ($request->filled('pendidikan')) {
|
||||
$pendidikanMap = [
|
||||
'tidak_sekolah' => ['01_tidak_sekolah'],
|
||||
'sd' => ['02_sd'],
|
||||
'smp' => ['03_smp'],
|
||||
'sma' => ['04_sma'],
|
||||
'diploma' => ['05_diploma'],
|
||||
's1_keatas' => ['06_sarjana', '07_magister'],
|
||||
];
|
||||
|
||||
if (isset($pendidikanMap[$request->pendidikan])) {
|
||||
$query->whereIn('pendidikan_terakhir', $pendidikanMap[$request->pendidikan]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($request->filled('jumlah_tanggungan')) {
|
||||
$tanggunganQuery = SosialEkonomi::query()
|
||||
->whereIn('petani_id', clone $filteredPetaniIds)
|
||||
->whereNotNull('jumlah_tanggungan')
|
||||
->select('petani_id')
|
||||
->distinct();
|
||||
|
||||
match ($request->jumlah_tanggungan) {
|
||||
'0' => $tanggunganQuery->where('jumlah_tanggungan', 0),
|
||||
'1_2' => $tanggunganQuery->whereBetween('jumlah_tanggungan', [1, 2]),
|
||||
'3_4' => $tanggunganQuery->whereBetween('jumlah_tanggungan', [3, 4]),
|
||||
'5_keatas' => $tanggunganQuery->where('jumlah_tanggungan', '>=', 5),
|
||||
default => null,
|
||||
};
|
||||
|
||||
$query->whereIn('petani.id', $tanggunganQuery);
|
||||
}
|
||||
|
||||
if ($request->filled('kelompok_tani')) {
|
||||
match ($request->kelompok_tani) {
|
||||
'terdaftar' => $query->whereIn('petani.id', SosialEkonomi::query()
|
||||
->whereIn('petani_id', clone $filteredPetaniIds)
|
||||
->where('terdaftar_kelompok_tani', 'ya')
|
||||
->select('petani_id')
|
||||
->distinct()),
|
||||
'tidak_terdaftar' => $query->whereIn('petani.id', SosialEkonomi::query()
|
||||
->whereIn('petani_id', clone $filteredPetaniIds)
|
||||
->where('terdaftar_kelompok_tani', 'tidak')
|
||||
->select('petani_id')
|
||||
->distinct()),
|
||||
'belum_terisi' => $query->whereNotIn('petani.id', SosialEkonomi::query()
|
||||
->whereIn('petani_id', clone $filteredPetaniIds)
|
||||
->whereIn('terdaftar_kelompok_tani', ['ya', 'tidak'])
|
||||
->select('petani_id')
|
||||
->distinct()),
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
|
||||
if ($request->filled('komoditas')) {
|
||||
$query->where('komoditas_id', $request->komoditas);
|
||||
}
|
||||
|
||||
if ($request->filled('penghasilan')) {
|
||||
[$min, $max] = match ($request->penghasilan) {
|
||||
'kurang_1' => [null, 999999],
|
||||
'1_3' => [1000000, 3000000],
|
||||
'3_5' => [3000001, 5000000],
|
||||
'5_10' => [5000001, 10000000],
|
||||
'lebih_10' => [10000001, null],
|
||||
default => [null, null],
|
||||
};
|
||||
|
||||
$query->whereIn('id', function ($sub) use ($min, $max) {
|
||||
$sub->from('pendapatan_komoditas')
|
||||
->join('sosial_ekonomi', 'sosial_ekonomi.id', '=', 'pendapatan_komoditas.sosial_ekonomi_id')
|
||||
->whereNotNull('sosial_ekonomi.petani_id')
|
||||
->groupBy('sosial_ekonomi.petani_id')
|
||||
->select('sosial_ekonomi.petani_id')
|
||||
->when($min !== null, fn ($q) => $q->havingRaw('SUM(pendapatan_komoditas.pendapatan) >= ?', [$min]))
|
||||
->when($max !== null, fn ($q) => $q->havingRaw('SUM(pendapatan_komoditas.pendapatan) <= ?', [$max]));
|
||||
});
|
||||
}
|
||||
|
||||
if ($request->filled('luas_lahan')) {
|
||||
$lahanQuery = Lahan::query()
|
||||
->whereIn('petani_id', clone $filteredPetaniIds)
|
||||
->select('petani_id')
|
||||
->distinct();
|
||||
|
||||
match ($request->luas_lahan) {
|
||||
'kurang_05' => $lahanQuery->where('luas_lahan', '<', 0.5),
|
||||
'05_1' => $lahanQuery->whereBetween('luas_lahan', [0.5, 1]),
|
||||
'1_2' => $lahanQuery->where('luas_lahan', '>', 1)->where('luas_lahan', '<=', 2),
|
||||
'lebih_2' => $lahanQuery->where('luas_lahan', '>', 2),
|
||||
default => null,
|
||||
};
|
||||
|
||||
$query->whereIn('petani.id', $lahanQuery);
|
||||
}
|
||||
|
||||
if ($request->filled('status_kepemilikan_lahan')) {
|
||||
$query->whereIn('petani.id', Lahan::query()
|
||||
->whereIn('petani_id', clone $filteredPetaniIds)
|
||||
->where('status_kepemilikan', $request->status_kepemilikan_lahan)
|
||||
->select('petani_id')
|
||||
->distinct());
|
||||
}
|
||||
|
||||
$daftarPetani = $query->orderBy('id')->paginate(10);
|
||||
|
||||
$pendidikanLabels = [
|
||||
'01_tidak_sekolah' => 'Tidak Sekolah',
|
||||
'02_sd' => 'SD',
|
||||
'03_smp' => 'SMP',
|
||||
'04_sma' => 'SMA',
|
||||
'05_diploma' => 'Diploma',
|
||||
'06_sarjana' => 'Sarjana',
|
||||
'07_magister' => 'Magister',
|
||||
];
|
||||
|
||||
$rows = collect($daftarPetani->items())->map(function ($petani) use ($pendidikanLabels) {
|
||||
$umur = $petani->tanggal_lahir ? \Carbon\Carbon::parse($petani->tanggal_lahir)->age : null;
|
||||
$pendidikanRaw = $petani->getRawOriginal('pendidikan_terakhir');
|
||||
$pendapatan = $petani->total_pendapatan;
|
||||
|
||||
$rentangPenghasilan = match (true) {
|
||||
$pendapatan === null => '-',
|
||||
$pendapatan < 1000000 => '< Rp1 juta',
|
||||
$pendapatan <= 3000000 => 'Rp1 - 3 juta',
|
||||
$pendapatan <= 5000000 => 'Rp3 - 5 juta',
|
||||
$pendapatan <= 10000000 => 'Rp5 - 10 juta',
|
||||
default => '> Rp10 juta',
|
||||
};
|
||||
|
||||
return [
|
||||
'id' => $petani->id,
|
||||
'nama' => $petani->nama ?? '-',
|
||||
'jenis_kelamin' => $petani->jenis_kelamin,
|
||||
'gender' => $petani->jenis_kelamin == 1 ? 'Laki-laki' : 'Perempuan',
|
||||
'umur' => $umur,
|
||||
'komoditas' => $petani->komoditas?->nama ?? '-',
|
||||
'luas_lahan' => (float) ($petani->total_luas_lahan ?? 0),
|
||||
'pendidikan' => $pendidikanLabels[$pendidikanRaw] ?? '-',
|
||||
'rentang_penghasilan' => $rentangPenghasilan,
|
||||
];
|
||||
});
|
||||
|
||||
return response()->json([
|
||||
'data' => $rows,
|
||||
'pagination' => [
|
||||
'current_page' => $daftarPetani->currentPage(),
|
||||
'last_page' => $daftarPetani->lastPage(),
|
||||
'per_page' => $daftarPetani->perPage(),
|
||||
'total' => $daftarPetani->total(),
|
||||
'from' => $daftarPetani->firstItem(),
|
||||
'to' => $daftarPetani->lastItem(),
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,590 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Web\Dashboard;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
use App\Models\Pertanian\Petani;
|
||||
use App\Models\Pertanian\Komoditas;
|
||||
use App\Models\Pertanian\Lahan;
|
||||
use App\Models\Pertanian\SkorPetani;
|
||||
|
||||
|
||||
class IndeksPetaniController extends Controller
|
||||
{
|
||||
|
||||
private function filters(Request $request): array
|
||||
{
|
||||
return [
|
||||
'desaKelurahan_id' => $request->desaKelurahan_id,
|
||||
'kecamatan_id' => $request->kecamatan_id,
|
||||
'kabupatenKota_id' => $request->kabupatenKota_id,
|
||||
'provinsi_id' => $request->provinsi_id,
|
||||
'komoditas_id' => $request->komoditas_id,
|
||||
'segmen' => $request->segmen,
|
||||
];
|
||||
}
|
||||
|
||||
private function petaniQuery(array $filters): Builder
|
||||
{
|
||||
$petaniQuery = Petani::query()
|
||||
->leftJoin('master_desa_kelurahan', 'petani.desa_kelurahan_id', '=', 'master_desa_kelurahan.id')
|
||||
->leftJoin('master_kecamatan', 'master_desa_kelurahan.kecamatan_id', '=', 'master_kecamatan.id')
|
||||
->leftJoin('master_kabupaten_kota', 'master_kecamatan.kabupaten_kota_id', '=', 'master_kabupaten_kota.id')
|
||||
->leftJoin('master_provinsi', 'master_kabupaten_kota.provinsi_id', '=', 'master_provinsi.id')
|
||||
->whereNull('petani.deleted_at')
|
||||
->when($filters['provinsi_id'], fn($q, $id) => $q->where('master_kabupaten_kota.provinsi_id', $id))
|
||||
->when($filters['kabupatenKota_id'], fn($q, $id) => $q->where('master_kecamatan.kabupaten_kota_id', $id))
|
||||
->when($filters['kecamatan_id'], fn($q, $id) => $q->where('master_desa_kelurahan.kecamatan_id', $id))
|
||||
->when($filters['desaKelurahan_id'], fn($q, $id) => $q->where('petani.desa_kelurahan_id', $id))
|
||||
->when($filters['komoditas_id'], fn($q, $id) => $q->where('petani.komoditas_id', $id));
|
||||
|
||||
if ($filters['segmen']) {
|
||||
$petaniQuery->whereHas('skor', fn (Builder $query) => $this->applySegmentFilter($query, $filters['segmen']));
|
||||
}
|
||||
|
||||
return $petaniQuery;
|
||||
}
|
||||
|
||||
private function skorPetaniQuery(array $filters, array $komoditas): Builder
|
||||
{
|
||||
$skor = SkorPetani::query()
|
||||
->join('petani', 'petani_skor.petani_id', '=', 'petani.id')
|
||||
->leftJoin('master_desa_kelurahan', 'petani.desa_kelurahan_id', '=', 'master_desa_kelurahan.id')
|
||||
->leftJoin('master_kecamatan', 'master_desa_kelurahan.kecamatan_id', '=', 'master_kecamatan.id')
|
||||
->leftJoin('master_kabupaten_kota', 'master_kecamatan.kabupaten_kota_id', '=', 'master_kabupaten_kota.id')
|
||||
->leftJoin('master_provinsi', 'master_kabupaten_kota.provinsi_id', '=', 'master_provinsi.id')
|
||||
->whereNull('petani.deleted_at')
|
||||
->when($filters['provinsi_id'], fn($q, $id) => $q->where('master_kabupaten_kota.provinsi_id', $id))
|
||||
->when($filters['kabupatenKota_id'], fn($q, $id) => $q->where('master_kecamatan.kabupaten_kota_id', $id))
|
||||
->when($filters['kecamatan_id'], fn($q, $id) => $q->where('master_desa_kelurahan.kecamatan_id', $id))
|
||||
->when($filters['desaKelurahan_id'], fn($q, $id) => $q->where('petani.desa_kelurahan_id', $id))
|
||||
->whereIn('petani.komoditas_id', $komoditas);
|
||||
|
||||
return $this->applySegmentFilter($skor, $filters['segmen']);
|
||||
}
|
||||
|
||||
private function applySegmentFilter(Builder $query, ?string $segment): Builder
|
||||
{
|
||||
$ranges = [
|
||||
'Penggerak' => [4, null],
|
||||
'Perintis' => [3, 4],
|
||||
'Pemerhati' => [2, 3],
|
||||
'Pemula' => [1, 2],
|
||||
];
|
||||
|
||||
if (isset($ranges[$segment])) {
|
||||
[$min, $max] = $ranges[$segment];
|
||||
|
||||
return $query->where('skor_kesiapan_adopsi', '>=', $min)
|
||||
->when($max !== null, fn (Builder $query) => $query->where('skor_kesiapan_adopsi', '<', $max));
|
||||
}
|
||||
|
||||
if ($segment === 'Belum Lengkap') {
|
||||
$query->where(fn (Builder $query) => $query->whereNull('skor_kesiapan_adopsi')
|
||||
->orWhere('skor_kesiapan_adopsi', '<', 1));
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function index(Request $request)
|
||||
{
|
||||
if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
return redirect()->route('dashboard.karakteristik-petani', array_merge($request->query(), [
|
||||
'tab' => 'indeks-petani',
|
||||
]));
|
||||
}
|
||||
|
||||
public function contentData(Request $request): array
|
||||
{
|
||||
$filters = $this->filters($request);
|
||||
|
||||
$segments = $this->segments();
|
||||
$colors = collect($segments)
|
||||
->pluck('color')
|
||||
->map(fn($c) => "var(--bs-{$c})")
|
||||
->values();
|
||||
|
||||
$komoditas = Komoditas::where('is_utama', true)->get(['id', 'nama']);
|
||||
$petaniQuery = $this->petaniQuery(array_fill_keys(array_keys($filters), null));
|
||||
|
||||
$desaKelurahan = $petaniQuery->clone()
|
||||
->selectRaw("
|
||||
master_desa_kelurahan.id as desa_kelurahan_id,
|
||||
master_desa_kelurahan.nama as desa_kelurahan,
|
||||
master_kecamatan.id as kecamatan_id,
|
||||
master_kecamatan.nama as kecamatan,
|
||||
master_kabupaten_kota.id as kabupaten_id,
|
||||
master_kabupaten_kota.nama as kabupaten,
|
||||
master_provinsi.id as provinsi_id,
|
||||
master_provinsi.nama as provinsi
|
||||
")
|
||||
->whereNotNull('master_provinsi.id')
|
||||
->distinct()
|
||||
->get();
|
||||
|
||||
$provinsi = $desaKelurahan
|
||||
->unique('provinsi_id')
|
||||
->sortBy('provinsi')
|
||||
->values();
|
||||
|
||||
$wilayah = $desaKelurahan
|
||||
->groupBy('provinsi_id')
|
||||
->map(function ($provinsi) {
|
||||
return [
|
||||
'id' => $provinsi->first()->provinsi_id,
|
||||
'nama' => $provinsi->first()->provinsi,
|
||||
'kabupaten' => $provinsi
|
||||
->groupBy('kabupaten_id')
|
||||
->map(function ($kabupaten) {
|
||||
return [
|
||||
'id' => $kabupaten->first()->kabupaten_id,
|
||||
'nama' => $kabupaten->first()->kabupaten,
|
||||
'kecamatan' => $kabupaten
|
||||
->groupBy('kecamatan_id')
|
||||
->map(function ($kecamatan) {
|
||||
return [
|
||||
'id' => $kecamatan->first()->kecamatan_id,
|
||||
'nama' => $kecamatan->first()->kecamatan,
|
||||
'desa' => $kecamatan->map(function ($desa) {
|
||||
return [
|
||||
'id' => $desa->desa_kelurahan_id,
|
||||
'nama' => $desa->desa_kelurahan,
|
||||
];
|
||||
})->values(),
|
||||
];
|
||||
})->values(),
|
||||
];
|
||||
})->values(),
|
||||
];
|
||||
})->values();
|
||||
|
||||
return array_merge($filters,
|
||||
compact(
|
||||
'komoditas',
|
||||
'desaKelurahan',
|
||||
'provinsi',
|
||||
'wilayah',
|
||||
'segments',
|
||||
'colors',
|
||||
));
|
||||
}
|
||||
|
||||
public function summary(Request $request): JsonResponse
|
||||
{
|
||||
$filters = $this->filters($request);
|
||||
$petaniQuery = $this->petaniQuery($filters);
|
||||
|
||||
$komoditas = Komoditas::where('is_utama', true)
|
||||
->when($filters['komoditas_id'], fn($q, $id) => $q->where('id', $id))->get(['id', 'nama']);
|
||||
|
||||
$counts = $petaniQuery->clone()
|
||||
->selectRaw('COUNT(*) as petani')
|
||||
->selectRaw('COUNT(DISTINCT petani.komoditas_id) FILTER (WHERE petani.komoditas_id IN (SELECT id FROM master_komoditas WHERE is_utama = true AND deleted_at IS NULL)) as komoditas')
|
||||
->selectRaw('COUNT(DISTINCT petani.desa_kelurahan_id) FILTER (WHERE petani.komoditas_id IN (SELECT id FROM master_komoditas WHERE is_utama = true AND deleted_at IS NULL)) as desa')
|
||||
->selectRaw('COUNT(DISTINCT master_kecamatan.id) FILTER (WHERE petani.komoditas_id IN (SELECT id FROM master_komoditas WHERE is_utama = true AND deleted_at IS NULL)) as kecamatan')
|
||||
->first();
|
||||
|
||||
$filteredPetaniIds = $petaniQuery->clone()->select('petani.id');
|
||||
$lahan = Lahan::query()
|
||||
->whereIn('petani_id', $filteredPetaniIds)
|
||||
->whereIn('komoditas_id', $komoditas->pluck('id'))
|
||||
->selectRaw('COALESCE(SUM(luas_lahan), 0) as total, AVG(luas_lahan) as rata_rata')
|
||||
->first();
|
||||
|
||||
$skorQuery = $this->skorPetaniQuery($filters, $komoditas->pluck('id')->toArray());
|
||||
$avgSkor = $skorQuery->clone()->avg('skor_kesiapan_adopsi');
|
||||
|
||||
if ($avgSkor >= 4.0) {
|
||||
$avgSegmen = 'Penggerak';
|
||||
} elseif ($avgSkor >= 3.0) {
|
||||
$avgSegmen = 'Perintis';
|
||||
} elseif ($avgSkor >= 2.0) {
|
||||
$avgSegmen = 'Pemerhati';
|
||||
} elseif ($avgSkor >= 1.0) {
|
||||
$avgSegmen = 'Pemula';
|
||||
} else {
|
||||
$avgSegmen = 'Belum Lengkap';
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'petani' => (int) $counts->petani,
|
||||
'komoditas' => (int) $counts->komoditas,
|
||||
'sum_lahan' => (float) $lahan->total,
|
||||
'avg_lahan' => (float) $lahan->rata_rata,
|
||||
'desa' => (int) $counts->desa,
|
||||
'kecamatan' => (int) $counts->kecamatan,
|
||||
'skor' => $avgSkor,
|
||||
'segmen' => $avgSegmen
|
||||
]);
|
||||
}
|
||||
|
||||
private function segments(): array
|
||||
{
|
||||
$segments = [
|
||||
[
|
||||
'name' => 'Penggerak',
|
||||
'range' => '4.0 - 5.0',
|
||||
'icon' => 'tabler-crown',
|
||||
'color' => 'success',
|
||||
],
|
||||
[
|
||||
'name' => 'Perintis',
|
||||
'range' => '3.0 - 3.9',
|
||||
'icon' => 'tabler-flag',
|
||||
'color' => 'info',
|
||||
],
|
||||
[
|
||||
'name' => 'Pemerhati',
|
||||
'range' => '2.0 - 2.9',
|
||||
'icon' => 'tabler-eye',
|
||||
'color' => 'warning',
|
||||
],
|
||||
[
|
||||
'name' => 'Pemula',
|
||||
'range' => '1.0 - 1.9',
|
||||
'icon' => 'tabler-seedling',
|
||||
'color' => 'danger',
|
||||
],
|
||||
];
|
||||
|
||||
return $segments;
|
||||
}
|
||||
|
||||
private function cases(): string
|
||||
{
|
||||
$case = "
|
||||
CASE
|
||||
WHEN skor_kesiapan_adopsi >= 4.0 THEN 'Penggerak'
|
||||
WHEN skor_kesiapan_adopsi >= 3.0 THEN 'Perintis'
|
||||
WHEN skor_kesiapan_adopsi >= 2.0 THEN 'Pemerhati'
|
||||
WHEN skor_kesiapan_adopsi >= 1.0 THEN 'Pemula'
|
||||
ELSE 'Belum Lengkap'
|
||||
END
|
||||
";
|
||||
|
||||
return $case;
|
||||
}
|
||||
public function segmen(Request $request): JsonResponse
|
||||
{
|
||||
$filters = $this->filters($request);
|
||||
$segments = $this->segments();
|
||||
$case = $this->cases();
|
||||
|
||||
$komoditas = Komoditas::where('is_utama', true)
|
||||
->when($filters['komoditas_id'], fn($q, $id) => $q->where('id', $id))->get(['id', 'nama']);
|
||||
|
||||
$skorQuery = $this->skorPetaniQuery($filters, $komoditas->pluck('id')->toArray());
|
||||
|
||||
$countSegmentasi = $skorQuery->clone()
|
||||
->selectRaw("$case AS segmen, COUNT(*) AS total")
|
||||
->groupByRaw($case)
|
||||
->get();
|
||||
|
||||
$series = collect($segments)
|
||||
->map(function ($segment) use ($countSegmentasi) {
|
||||
return $countSegmentasi
|
||||
->where('segmen', $segment['name'])
|
||||
->sum('total');
|
||||
})
|
||||
->values();
|
||||
|
||||
$countPetani = $series->sum();
|
||||
|
||||
$persentase = collect($segments)
|
||||
->map(function ($segment) use ($countSegmentasi, $countPetani) {
|
||||
$total = $countSegmentasi
|
||||
->where('segmen', $segment['name'])
|
||||
->sum('total');
|
||||
|
||||
return $countPetani > 0
|
||||
? ($total / $countPetani) * 100
|
||||
: 0;
|
||||
})
|
||||
->values();
|
||||
|
||||
return response()->json([
|
||||
'series' => $series,
|
||||
'persentase' => $persentase,
|
||||
]);
|
||||
}
|
||||
|
||||
public function segmenByKomoditas(Request $request): JsonResponse
|
||||
{
|
||||
$filters = $this->filters($request);
|
||||
$segments = $this->segments();
|
||||
$case = $this->cases();
|
||||
|
||||
$komoditas = Komoditas::where('is_utama', true)
|
||||
->when($filters['komoditas_id'], fn($q, $id) => $q->where('id', $id))->get(['id', 'nama']);
|
||||
|
||||
$skorQuery = $this->skorPetaniQuery($filters, $komoditas->pluck('id')->toArray());
|
||||
|
||||
$countSegmentasiByKomoditas = $skorQuery->clone()
|
||||
->selectRaw("
|
||||
petani.komoditas_id,
|
||||
$case AS segmen,
|
||||
COUNT(petani.id) AS total
|
||||
")
|
||||
->groupBy('petani.komoditas_id')
|
||||
->groupByRaw($case)
|
||||
->get();
|
||||
|
||||
$seriesSegmentasiKomoditas = collect($segments)->map(function ($segment) use ($countSegmentasiByKomoditas, $komoditas) {
|
||||
return [
|
||||
'name' => $segment['name'],
|
||||
'data' => $komoditas->map(function ($item) use ($countSegmentasiByKomoditas, $segment) {
|
||||
|
||||
return optional(
|
||||
$countSegmentasiByKomoditas->first(function ($row) use ($item, $segment) {
|
||||
return $row->komoditas_id == $item->id
|
||||
&& $row->segmen == $segment['name'];
|
||||
})
|
||||
)->total ?? 0;
|
||||
|
||||
})->values(),
|
||||
];
|
||||
})->values();
|
||||
|
||||
return response()->json([
|
||||
'categories' => $komoditas->pluck('nama')->values(),
|
||||
'series' => $seriesSegmentasiKomoditas,
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
public function dimensi(Request $request): JsonResponse
|
||||
{
|
||||
$filters = $this->filters($request);
|
||||
|
||||
$komoditas = Komoditas::where('is_utama', true)
|
||||
->when($filters['komoditas_id'], fn($q, $id) => $q->where('id', $id))->get(['id', 'nama']);
|
||||
|
||||
$skorQuery = $this->skorPetaniQuery($filters, $komoditas->pluck('id')->toArray());
|
||||
|
||||
$avgDimensi = $skorQuery->clone()
|
||||
->selectRaw("
|
||||
avg(skor_d2_sistem_praktik_budidaya) as skor_d2_sistem_praktik_budidaya,
|
||||
avg(skor_d4_sosial_ekonomi) as skor_d4_sosial_ekonomi,
|
||||
avg(skor_d6_rantai_nilai_akses_pasar) as skor_d6_rantai_nilai_akses_pasar")
|
||||
->first();
|
||||
|
||||
return response()->json([
|
||||
'radarDimensi' => [
|
||||
round($avgDimensi->skor_d2_sistem_praktik_budidaya, 2),
|
||||
round($avgDimensi->skor_d4_sosial_ekonomi, 2),
|
||||
round($avgDimensi->skor_d6_rantai_nilai_akses_pasar, 2),
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
public function komponen(Request $request): JsonResponse
|
||||
{
|
||||
$filters = $this->filters($request);
|
||||
|
||||
$komoditas = Komoditas::where('is_utama', true)
|
||||
->when($filters['komoditas_id'], fn($q, $id) => $q->where('id', $id))->get(['id', 'nama']);
|
||||
|
||||
$skorQuery = $this->skorPetaniQuery($filters, $komoditas->pluck('id')->toArray());
|
||||
|
||||
$avgKomponen = $skorQuery->clone()
|
||||
->selectRaw("avg(skor_komp_1_d4_k3_motivasi_kapasitas_adopsi) as skor_komp_1_d4_k3_motivasi_kapasitas_adopsi,
|
||||
avg(skor_komp_2_d4_k2_ekonomi_rumah_tangga) as skor_komp_2_d4_k2_ekonomi_rumah_tangga,
|
||||
avg(skor_komp_3_d2_praktik_budidaya_gap) as skor_komp_3_d2_praktik_budidaya_gap,
|
||||
avg(skor_komp_4_d4_k4_keterlibatan_kelembagaan) as skor_komp_4_d4_k4_keterlibatan_kelembagaan,
|
||||
avg(skor_komp_5_d4_k5_k6_akses_kebebasan_keuangan) as skor_komp_5_d4_k5_k6_akses_kebebasan_keuangan,
|
||||
avg(skor_komp_6_d6_k1_k4_k5_rantai_nilai_akses_pasar) as skor_komp_6_d6_k1_k4_k5_rantai_nilai_akses_pasar")
|
||||
->first();
|
||||
|
||||
return response()->json([
|
||||
'radarKomponen' => [
|
||||
round($avgKomponen->skor_komp_1_d4_k3_motivasi_kapasitas_adopsi, 2),
|
||||
round($avgKomponen->skor_komp_2_d4_k2_ekonomi_rumah_tangga, 2),
|
||||
round($avgKomponen->skor_komp_3_d2_praktik_budidaya_gap, 2),
|
||||
round($avgKomponen->skor_komp_4_d4_k4_keterlibatan_kelembagaan, 2),
|
||||
round($avgKomponen->skor_komp_5_d4_k5_k6_akses_kebebasan_keuangan, 2),
|
||||
round($avgKomponen->skor_komp_6_d6_k1_k4_k5_rantai_nilai_akses_pasar, 2)
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
private function villageData(array $filters, bool $includePath = true): \Illuminate\Support\Collection
|
||||
{
|
||||
$case = $this->cases();
|
||||
|
||||
$petaniQuery = $this->petaniQuery($filters);
|
||||
|
||||
$desaKelurahan = $petaniQuery->clone()
|
||||
->selectRaw("
|
||||
master_desa_kelurahan.id as desa_kelurahan_id,
|
||||
master_desa_kelurahan.nama as desa_kelurahan,
|
||||
master_kecamatan.id as kecamatan_id,
|
||||
master_kecamatan.nama as kecamatan,
|
||||
master_kabupaten_kota.id as kabupaten_id,
|
||||
master_kabupaten_kota.nama as kabupaten,
|
||||
master_provinsi.id as provinsi_id,
|
||||
master_provinsi.nama as provinsi
|
||||
")
|
||||
->when($includePath, fn (Builder $query) => $query->addSelect('master_desa_kelurahan.path as path'))
|
||||
->distinct()
|
||||
->get();
|
||||
|
||||
$komoditas = Komoditas::where('is_utama', true)
|
||||
->when($filters['komoditas_id'], fn($q, $id) => $q->where('id', $id))->get(['id', 'nama']);
|
||||
|
||||
$skorQuery = $this->skorPetaniQuery($filters, $komoditas->pluck('id')->toArray());
|
||||
|
||||
$mapSegmentasi = $skorQuery->clone()
|
||||
->selectRaw("petani.desa_kelurahan_id,
|
||||
$case AS segmen,
|
||||
COUNT(petani.id) AS total
|
||||
")
|
||||
->groupBy('petani.desa_kelurahan_id')
|
||||
->groupByRaw($case)
|
||||
->get();
|
||||
|
||||
$mapAvgSkorSegmentasi = $skorQuery->clone()
|
||||
->selectRaw("petani.desa_kelurahan_id,
|
||||
COUNT(petani.id) AS jumlah_petani,
|
||||
avg(skor_kesiapan_adopsi) AS skor,
|
||||
CASE
|
||||
WHEN AVG(skor_kesiapan_adopsi) >= 4 THEN 'Penggerak'
|
||||
WHEN AVG(skor_kesiapan_adopsi) >= 3 THEN 'Perintis'
|
||||
WHEN AVG(skor_kesiapan_adopsi) >= 2 THEN 'Pemerhati'
|
||||
WHEN AVG(skor_kesiapan_adopsi) >= 1 THEN 'Pemula'
|
||||
ELSE 'Belum Lengkap'
|
||||
END AS segmen
|
||||
")
|
||||
->groupBy('petani.desa_kelurahan_id')
|
||||
->get();
|
||||
|
||||
$segmentasiGroups = $mapSegmentasi->groupBy('desa_kelurahan_id');
|
||||
$jumlahSegmenByDesa = $segmentasiGroups
|
||||
->map(fn ($items) => $items->pluck('total', 'segmen'));
|
||||
|
||||
$segmenDominan = $segmentasiGroups
|
||||
->map(function ($items) {
|
||||
return $items->reduce(fn ($largest, $item) => $largest === null || $item->total > $largest->total ? $item : $largest);
|
||||
});
|
||||
|
||||
$mapAvgSkorSegmentasi->each(function ($item) use ($segmenDominan, $jumlahSegmenByDesa) {
|
||||
$dominan = $segmenDominan->get($item->desa_kelurahan_id);
|
||||
|
||||
$item->segmen_dominan = $dominan?->segmen;
|
||||
$item->jumlah_dominan = $dominan?->total;
|
||||
$item->jumlah_segmen = $jumlahSegmenByDesa->get($item->desa_kelurahan_id, collect());
|
||||
});
|
||||
|
||||
$segmentasiByDesa = $mapAvgSkorSegmentasi->keyBy('desa_kelurahan_id');
|
||||
$desaLookup = $desaKelurahan->keyBy('desa_kelurahan_id');
|
||||
|
||||
$mapDesaSegmentasi = $segmentasiByDesa->map(function ($segmentasi, $desaId) use ($desaLookup) {
|
||||
$desa = $desaLookup->get($desaId);
|
||||
if (!$desa) {
|
||||
return null;
|
||||
}
|
||||
$desa->segmentasi = $segmentasi;
|
||||
|
||||
return $desa;
|
||||
})->filter()->values();
|
||||
|
||||
return $mapDesaSegmentasi;
|
||||
}
|
||||
|
||||
public function map(Request $request): JsonResponse
|
||||
{
|
||||
return response()->json($this->villageData($this->filters($request)));
|
||||
}
|
||||
|
||||
public function desa(Request $request): JsonResponse
|
||||
{
|
||||
$segmentStyles = collect($this->segments())->keyBy('name');
|
||||
$mapDesaSegmentasi = $this->villageData($this->filters($request), false);
|
||||
|
||||
$segmentNames = ['Pemula', 'Perintis', 'Pemerhati', 'Penggerak', 'Belum Lengkap'];
|
||||
$rows = $mapDesaSegmentasi->map(function ($desa) use ($segmentStyles, $segmentNames) {
|
||||
$total = (int) $desa->segmentasi->jumlah_petani;
|
||||
|
||||
$style = $segmentStyles[$desa->segmentasi->segmen] ?? ['color' => 'secondary'];
|
||||
$styleDominan = $segmentStyles[$desa->segmentasi->segmen_dominan] ?? ['color' => 'secondary'];
|
||||
|
||||
return [
|
||||
'desa' => $desa->desa_kelurahan,
|
||||
'jumlah_petani' => $desa->segmentasi->jumlah_petani,
|
||||
'segmen' => $desa->segmentasi->segmen,
|
||||
'skor' => $desa->segmentasi->skor,
|
||||
'segmen_dominan' => $desa->segmentasi->segmen_dominan,
|
||||
'jumlah_dominan' => $desa->segmentasi->jumlah_dominan,
|
||||
'color' => $style['color'],
|
||||
'color_dominan' => $styleDominan['color'],
|
||||
'segments' => collect($segmentNames)
|
||||
->mapWithKeys(function ($name) use ($desa, $segmentStyles, $total) {
|
||||
$count = (int) $desa->segmentasi->jumlah_segmen->get($name, 0);
|
||||
|
||||
return [strtolower(str_replace(' ', '_', $name)) => [
|
||||
'count' => $count,
|
||||
'percentage' => $total > 0 ? round($count / $total * 100, 2) : 0,
|
||||
'color' => $segmentStyles[$name]['color'] ?? 'secondary',
|
||||
]];
|
||||
})->all(),
|
||||
];
|
||||
});
|
||||
|
||||
return response()->json($rows);
|
||||
}
|
||||
|
||||
public function data(Request $request): JsonResponse
|
||||
{
|
||||
$filters = $this->filters($request);
|
||||
$segments = $this->segments();
|
||||
|
||||
$segmentStyles = collect($segments)->keyBy('name');
|
||||
|
||||
$petaniQuery = $this->petaniQuery($filters);
|
||||
|
||||
$komoditas = Komoditas::where('is_utama', true)
|
||||
->when($filters['komoditas_id'], fn($q, $id) => $q->where('id', $id))->get(['id', 'nama']);
|
||||
|
||||
$daftarPetani = $petaniQuery->clone()
|
||||
->select('petani.*')
|
||||
->with(['desaKelurahan', 'komoditas', 'skor'])
|
||||
->withSum('lahans as total_luas_lahan', 'luas_lahan')
|
||||
->whereIn('komoditas_id', $komoditas->pluck('id'))
|
||||
->get();
|
||||
|
||||
$data = $daftarPetani
|
||||
->map(function ($item) use ($segmentStyles) {
|
||||
$segmen = $item->skor
|
||||
? $item->segmen()
|
||||
: null;
|
||||
$style = $segmentStyles[$segmen]
|
||||
?? ['color' => 'secondary'];
|
||||
|
||||
return [
|
||||
'id' => $item->id,
|
||||
'nama' => $item->nama,
|
||||
'desa' => $item->desaKelurahan?->nama,
|
||||
'desa_kelurahan_id' => $item->desa_kelurahan_id,
|
||||
'komoditas' => $item->komoditas?->nama,
|
||||
'luas_lahan' => (float) ($item->total_luas_lahan ?? 0),
|
||||
'skor' => $item->skor
|
||||
? $item->skor->skor_kesiapan_adopsi
|
||||
: null,
|
||||
'segmen' => $segmen,
|
||||
'color' => $style['color'],
|
||||
'url' => route('dashboard.profil-petani', $item->id),
|
||||
];
|
||||
})
|
||||
->values();
|
||||
|
||||
return response()->json($data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,769 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Web\Dashboard;
|
||||
|
||||
use App\Enums\KeparahanSerangan;
|
||||
use App\Enums\PenyakitTanaman;
|
||||
use App\Enums\PraktikBudidaya\PemberianPupuk;
|
||||
use App\Enums\SeranganHama;
|
||||
use App\Enums\SosialEkonomi\StatusKepemilikanLahanSosialEkonomi;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Pertanian\Komoditas;
|
||||
use App\Models\Pertanian\Lahan;
|
||||
use App\Models\Pertanian\Petani;
|
||||
use App\Models\Pertanian\PraktikBudidaya;
|
||||
use App\Models\Pertanian\Produksi;
|
||||
use App\Models\Pertanian\SkorPetani;
|
||||
use App\Models\Pertanian\SosialEkonomi;
|
||||
use App\Models\Pertanian\SurveyProduktivitas;
|
||||
use App\Models\Wilayah\DesaKelurahan;
|
||||
use App\Models\Wilayah\KabupatenKota;
|
||||
use App\Models\Wilayah\Kecamatan;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class IndikatorPertanianController extends Controller
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
$kabupatenOptions = $this->kabupatenOptions();
|
||||
$ngadaOption = collect($kabupatenOptions)->first(
|
||||
fn ($option) => str_contains(strtolower($option['label']), 'ngada')
|
||||
);
|
||||
$initialFilters = $this->selectedFilters($request, $ngadaOption['value'] ?? 'all');
|
||||
|
||||
$totalPetani = $this->applyPetaniFilters(Petani::query(), $initialFilters)->count();
|
||||
$totalLuasLahan = ((float) $this->applyLahanFilters(Lahan::query(), $initialFilters)->sum('luas_lahan')) / 10000;
|
||||
$rataRataLuasLahan = $totalPetani > 0
|
||||
? $totalLuasLahan / $totalPetani
|
||||
: 0;
|
||||
$totalProduksiKg = $this->totalProduksiKg($initialFilters);
|
||||
$rataRataProduksiKg = $totalPetani > 0
|
||||
? $totalProduksiKg / $totalPetani
|
||||
: 0;
|
||||
$rataRataProduktivitas = $totalLuasLahan > 0
|
||||
? $totalProduksiKg / $totalLuasLahan
|
||||
: 0;
|
||||
|
||||
$filterOptions = [
|
||||
[
|
||||
'key' => 'kabupaten',
|
||||
'label' => 'Kabupaten',
|
||||
'options' => $kabupatenOptions,
|
||||
'icon' => 'tabler-map-pin',
|
||||
'readonly' => true,
|
||||
],
|
||||
[
|
||||
'key' => 'kecamatan',
|
||||
'label' => 'Kecamatan',
|
||||
'options' => $this->kecamatanOptions(),
|
||||
],
|
||||
[
|
||||
'key' => 'desa',
|
||||
'label' => 'Desa',
|
||||
'options' => $this->desaOptions(),
|
||||
],
|
||||
[
|
||||
'key' => 'komoditas',
|
||||
'label' => 'Komoditas',
|
||||
'options' => $this->komoditasOptions(),
|
||||
],
|
||||
[
|
||||
'key' => 'segmentasi',
|
||||
'label' => 'Segmentasi',
|
||||
'options' => $this->segmentasiOptions(),
|
||||
],
|
||||
];
|
||||
|
||||
$summaryData = [
|
||||
[
|
||||
'value' => number_format($totalPetani, 0, ',', '.'),
|
||||
'label' => 'Petani Terdata',
|
||||
'description' => 'Total petani yang masuk sistem',
|
||||
'icon' => 'tabler-users',
|
||||
'variant' => 'primary',
|
||||
],
|
||||
[
|
||||
'value' => number_format($totalLuasLahan, 2, ',', '.').' ha',
|
||||
'label' => 'Total Luas Lahan',
|
||||
'description' => 'Rata-rata '.number_format($rataRataLuasLahan, 2, ',', '.').' ha/petani',
|
||||
'icon' => 'tabler-mountain',
|
||||
'variant' => 'success',
|
||||
],
|
||||
[
|
||||
'value' => number_format($totalProduksiKg, 0, ',', '.').' kg',
|
||||
'label' => 'Total Produksi (Semua Komoditas)',
|
||||
'description' => 'Rata-rata '.number_format($rataRataProduksiKg, 0, ',', '.').' kg/petani',
|
||||
'icon' => 'tabler-wheat',
|
||||
'variant' => 'warning',
|
||||
],
|
||||
[
|
||||
'value' => number_format($rataRataProduktivitas, 0, ',', '.').' kg/ha',
|
||||
'label' => 'Rata-rata Produktivitas',
|
||||
'description' => 'Semua komoditas berdasarkan total produksi dan luas lahan',
|
||||
'icon' => 'tabler-chart-bar',
|
||||
'variant' => 'info',
|
||||
],
|
||||
];
|
||||
|
||||
return view('dashboard.indikator-pertanian', [
|
||||
'title' => 'Indikator Pertanian',
|
||||
'initialFilters' => $initialFilters,
|
||||
'filterOptions' => $filterOptions,
|
||||
'summaryData' => $summaryData,
|
||||
'chartData' => [
|
||||
'petaniPerKomoditas' => $this->petaniPerKomoditasChart($initialFilters),
|
||||
'luasLahanPerKecamatan' => $this->luasLahanPerKecamatanChart($initialFilters),
|
||||
'kondisiTanaman' => $this->kondisiTanamanChart($initialFilters),
|
||||
'statusKepemilikanLahan' => $this->statusKepemilikanLahanChart($initialFilters),
|
||||
'seranganHama' => $this->seranganHamaChart($initialFilters),
|
||||
'penyakitTanaman' => $this->penyakitTanamanChart($initialFilters),
|
||||
'keparahanSerangan' => $this->keparahanSeranganChart($initialFilters),
|
||||
'statusHakKelolaLahan' => $this->statusHakKelolaLahanChart($initialFilters),
|
||||
'praktikPemupukan' => $this->praktikPemupukanChart($initialFilters),
|
||||
'produksiPerKomoditas' => $this->produksiPerKomoditasChart($initialFilters),
|
||||
'produktivitasPerKomoditas' => $this->produktivitasPerKomoditasChart($initialFilters),
|
||||
'luasPanenPerKomoditas' => $this->luasPanenPerKomoditasChart($initialFilters),
|
||||
'statusPanen' => $this->statusPanenChart($initialFilters),
|
||||
'surveyProduktivitasHa' => $this->surveyProduktivitasByUnitChart($initialFilters, 'ha_tahun'),
|
||||
'surveyProduktivitasPohon' => $this->surveyProduktivitasByUnitChart($initialFilters, 'pohon_tahun'),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
private function selectedFilters(Request $request, string $defaultKabupaten): array
|
||||
{
|
||||
$filters = [
|
||||
'kabupaten' => $defaultKabupaten,
|
||||
'kecamatan' => $this->validFilterValue($request->query('kecamatan'), Kecamatan::class),
|
||||
'desa' => $this->validFilterValue($request->query('desa'), DesaKelurahan::class),
|
||||
'komoditas' => $this->validFilterValue($request->query('komoditas'), Komoditas::class),
|
||||
'segmentasi' => $this->validSegmentasi((string) $request->query('segmentasi', 'all')),
|
||||
];
|
||||
|
||||
if ($filters['desa'] !== 'all') {
|
||||
$desa = DesaKelurahan::query()->find($filters['desa']);
|
||||
|
||||
if (! $desa || ($filters['kecamatan'] !== 'all' && (string) $desa->kecamatan_id !== $filters['kecamatan'])) {
|
||||
$filters['desa'] = 'all';
|
||||
}
|
||||
}
|
||||
|
||||
if ($filters['kecamatan'] === 'all') {
|
||||
$filters['desa'] = 'all';
|
||||
}
|
||||
|
||||
return $filters;
|
||||
}
|
||||
|
||||
private function validFilterValue(mixed $value, string $modelClass): string
|
||||
{
|
||||
if (! $value || $value === 'all') {
|
||||
return 'all';
|
||||
}
|
||||
|
||||
return $modelClass::query()->whereKey($value)->exists()
|
||||
? (string) $value
|
||||
: 'all';
|
||||
}
|
||||
|
||||
private function validSegmentasi(string $value): string
|
||||
{
|
||||
return in_array($value, [
|
||||
'Penggerak',
|
||||
'Perintis',
|
||||
'Pemerhati',
|
||||
'Pemula',
|
||||
'Belum tersegmentasi',
|
||||
'all',
|
||||
], true) ? $value : 'all';
|
||||
}
|
||||
|
||||
private function kabupatenOptions(): array
|
||||
{
|
||||
return $this->withAllOption(
|
||||
'Semua Kabupaten',
|
||||
KabupatenKota::query()
|
||||
->orderBy('nama')
|
||||
->get(['id', 'kode', 'nama'])
|
||||
->map(fn (KabupatenKota $kabupaten) => [
|
||||
'value' => (string) $kabupaten->id,
|
||||
'label' => $this->kabupatenLabel($kabupaten),
|
||||
])
|
||||
->values()
|
||||
->all()
|
||||
);
|
||||
}
|
||||
|
||||
private function kecamatanOptions(): array
|
||||
{
|
||||
return $this->withAllOption(
|
||||
'Semua Kecamatan',
|
||||
Kecamatan::query()
|
||||
->orderBy('nama')
|
||||
->get(['id', 'nama'])
|
||||
->map(fn (Kecamatan $kecamatan) => [
|
||||
'value' => (string) $kecamatan->id,
|
||||
'label' => $kecamatan->nama,
|
||||
])
|
||||
->values()
|
||||
->all()
|
||||
);
|
||||
}
|
||||
|
||||
private function desaOptions(): array
|
||||
{
|
||||
return $this->withAllOption(
|
||||
'Semua Desa',
|
||||
DesaKelurahan::query()
|
||||
->orderBy('nama')
|
||||
->get(['id', 'nama', 'kecamatan_id'])
|
||||
->map(fn (DesaKelurahan $desa) => [
|
||||
'value' => (string) $desa->id,
|
||||
'label' => $desa->nama,
|
||||
'parent' => (string) $desa->kecamatan_id,
|
||||
])
|
||||
->values()
|
||||
->all()
|
||||
);
|
||||
}
|
||||
|
||||
private function komoditasOptions(): array
|
||||
{
|
||||
return $this->withAllOption(
|
||||
'Semua Komoditas',
|
||||
Komoditas::query()
|
||||
->orderBy('nama')
|
||||
->get(['id', 'nama'])
|
||||
->map(fn (Komoditas $komoditas) => [
|
||||
'value' => (string) $komoditas->id,
|
||||
'label' => $komoditas->nama,
|
||||
])
|
||||
->values()
|
||||
->all()
|
||||
);
|
||||
}
|
||||
|
||||
private function segmentasiOptions(): array
|
||||
{
|
||||
// Aggregate bucket counts in SQL instead of pulling every skor_kesiapan_adopsi
|
||||
// value into PHP just to map/filter/unique it.
|
||||
$row = SkorPetani::query()->selectRaw("
|
||||
COUNT(*) FILTER (WHERE skor_kesiapan_adopsi >= 4) as penggerak,
|
||||
COUNT(*) FILTER (WHERE skor_kesiapan_adopsi >= 3 AND skor_kesiapan_adopsi < 4) as perintis,
|
||||
COUNT(*) FILTER (WHERE skor_kesiapan_adopsi >= 2 AND skor_kesiapan_adopsi < 3) as pemerhati,
|
||||
COUNT(*) FILTER (WHERE skor_kesiapan_adopsi >= 1 AND skor_kesiapan_adopsi < 2) as pemula,
|
||||
COUNT(*) FILTER (WHERE skor_kesiapan_adopsi IS NOT NULL AND skor_kesiapan_adopsi < 1) as belum_tersegmentasi
|
||||
")->first();
|
||||
|
||||
$order = [
|
||||
'Penggerak' => $row->penggerak,
|
||||
'Perintis' => $row->perintis,
|
||||
'Pemerhati' => $row->pemerhati,
|
||||
'Pemula' => $row->pemula,
|
||||
'Belum tersegmentasi' => $row->belum_tersegmentasi,
|
||||
];
|
||||
|
||||
$segments = collect($order)
|
||||
->filter(fn ($count) => $count > 0)
|
||||
->keys()
|
||||
->map(fn ($segment) => [
|
||||
'value' => $segment,
|
||||
'label' => $segment,
|
||||
])
|
||||
->values()
|
||||
->all();
|
||||
|
||||
return $this->withAllOption('Semua Segmentasi', $segments);
|
||||
}
|
||||
|
||||
private function totalProduksiKg(array $filters): float
|
||||
{
|
||||
$expr = $this->kgCaseExpr('jumlah_panen', 'satuan');
|
||||
|
||||
return (float) ($this->applyProduksiFilters(Produksi::query(), $filters)
|
||||
->whereNotNull('jumlah_panen')
|
||||
->selectRaw("SUM({$expr}) as total")
|
||||
->value('total') ?? 0);
|
||||
}
|
||||
|
||||
private function petaniPerKomoditasChart(array $filters): array
|
||||
{
|
||||
$rows = $this->applyPetaniFilters(Petani::query(), $filters)
|
||||
->leftJoin('master_komoditas as k', 'k.id', '=', 'petani.komoditas_id')
|
||||
->selectRaw("COALESCE(k.nama, 'Belum ditentukan') as label")
|
||||
->selectRaw('COUNT(petani.id) as total')
|
||||
->groupBy('k.id', 'k.nama')
|
||||
->orderByDesc('total')
|
||||
->get();
|
||||
|
||||
return [
|
||||
'labels' => $rows->pluck('label')->values()->all(),
|
||||
'series' => $rows->pluck('total')->map(fn ($value) => (int) $value)->values()->all(),
|
||||
];
|
||||
}
|
||||
|
||||
private function luasLahanPerKecamatanChart(array $filters): array
|
||||
{
|
||||
$rows = $this->applyLahanFilters(Lahan::query(), $filters)
|
||||
->leftJoin('master_desa_kelurahan as d', 'd.id', '=', 'map_lahan.desa_kelurahan_id')
|
||||
->leftJoin('master_kecamatan as k', 'k.id', '=', 'd.kecamatan_id')
|
||||
->selectRaw("COALESCE(k.nama, 'Belum ditentukan') as label")
|
||||
->selectRaw('SUM(map_lahan.luas_lahan) / 10000 as total')
|
||||
->groupBy('k.id', 'k.nama')
|
||||
->havingRaw('SUM(map_lahan.luas_lahan) > 0')
|
||||
->orderByDesc('total')
|
||||
->limit(8)
|
||||
->get()
|
||||
->filter(fn ($row) => round((float) $row->total, 2) > 0)
|
||||
->values();
|
||||
|
||||
return [
|
||||
'labels' => $rows->pluck('label')->values()->all(),
|
||||
'series' => $rows->pluck('total')->map(fn ($value) => round((float) $value, 2))->values()->all(),
|
||||
];
|
||||
}
|
||||
|
||||
private function kondisiTanamanChart(array $filters): array
|
||||
{
|
||||
$rows = $this->applyTanamanTableFilters(DB::table('map_tanaman'), $filters)
|
||||
->whereNull('map_tanaman.deleted_at')
|
||||
->whereNotNull('map_tanaman.kondisi_tanaman')
|
||||
->selectRaw('map_tanaman.kondisi_tanaman as label')
|
||||
->selectRaw('COUNT(map_tanaman.id) as total')
|
||||
->groupBy('map_tanaman.kondisi_tanaman')
|
||||
->orderByDesc('total')
|
||||
->get();
|
||||
|
||||
if ($rows->isEmpty()) {
|
||||
$rows = $this->applyLahanFilters(Lahan::query(), $filters)
|
||||
->whereNull('deleted_at')
|
||||
->whereNotNull('kondisi_tanaman')
|
||||
->selectRaw('kondisi_tanaman as label')
|
||||
->selectRaw('COUNT(map_lahan.id) as total')
|
||||
->groupBy('kondisi_tanaman')
|
||||
->orderByDesc('total')
|
||||
->get();
|
||||
}
|
||||
|
||||
return [
|
||||
'labels' => $rows->pluck('label')->map(fn ($label) => $this->shortEnumLabel((string) $label))->values()->all(),
|
||||
'series' => $rows->pluck('total')->map(fn ($value) => (int) $value)->values()->all(),
|
||||
];
|
||||
}
|
||||
|
||||
private function seranganHamaChart(array $filters): array
|
||||
{
|
||||
return $this->tanamanAttributeChart(
|
||||
$filters,
|
||||
'serangan_hama',
|
||||
fn ($label) => $this->enumLabel(SeranganHama::class, (string) $label)
|
||||
);
|
||||
}
|
||||
|
||||
private function penyakitTanamanChart(array $filters): array
|
||||
{
|
||||
return $this->tanamanAttributeChart(
|
||||
$filters,
|
||||
'penyakit_tanaman',
|
||||
fn ($label) => $this->enumLabel(PenyakitTanaman::class, (string) $label)
|
||||
);
|
||||
}
|
||||
|
||||
private function keparahanSeranganChart(array $filters): array
|
||||
{
|
||||
return $this->tanamanAttributeChart(
|
||||
$filters,
|
||||
'keparahan_serangan',
|
||||
fn ($label) => $this->keparahanSeranganLabel((string) $label)
|
||||
);
|
||||
}
|
||||
|
||||
private function statusKepemilikanLahanChart(array $filters): array
|
||||
{
|
||||
$rows = $this->applyLahanFilters(Lahan::query(), $filters)
|
||||
->whereNotNull('status_kepemilikan')
|
||||
->selectRaw('status_kepemilikan as label')
|
||||
->selectRaw('COUNT(map_lahan.id) as total')
|
||||
->groupBy('status_kepemilikan')
|
||||
->orderByDesc('total')
|
||||
->get();
|
||||
|
||||
return $this->chartFromRows($rows, fn ($label) => $this->shortEnumLabel((string) $label));
|
||||
}
|
||||
|
||||
private function statusHakKelolaLahanChart(array $filters): array
|
||||
{
|
||||
$rows = $this->applySosialEkonomiFilters(SosialEkonomi::query(), $filters)
|
||||
->whereNotNull('status_kepemilikan_lahan')
|
||||
->selectRaw('status_kepemilikan_lahan as label')
|
||||
->selectRaw('COUNT(sosial_ekonomi.id) as total')
|
||||
->groupBy('status_kepemilikan_lahan')
|
||||
->orderByDesc('total')
|
||||
->get();
|
||||
|
||||
return $this->chartFromRows(
|
||||
$rows,
|
||||
fn ($label) => $this->enumLabel(StatusKepemilikanLahanSosialEkonomi::class, (string) $label)
|
||||
);
|
||||
}
|
||||
|
||||
private function praktikPemupukanChart(array $filters): array
|
||||
{
|
||||
$rows = $this->applyPraktikBudidayaFilters(PraktikBudidaya::query(), $filters)
|
||||
->whereNotNull('pemberian_pupuk')
|
||||
->selectRaw('pemberian_pupuk as label')
|
||||
->selectRaw('COUNT(praktik_budidaya.id) as total')
|
||||
->groupBy('pemberian_pupuk')
|
||||
->orderBy('pemberian_pupuk')
|
||||
->get();
|
||||
|
||||
return $this->chartFromRows(
|
||||
$rows,
|
||||
fn ($label) => $this->pemberianPupukLabel((int) $label)
|
||||
);
|
||||
}
|
||||
|
||||
private function produksiPerKomoditasChart(array $filters): array
|
||||
{
|
||||
$expr = $this->kgCaseExpr('produksi.jumlah_panen', 'produksi.satuan');
|
||||
|
||||
$rows = $this->applyProduksiFilters(Produksi::query(), $filters)
|
||||
->leftJoin('master_komoditas as k', 'k.id', '=', 'produksi.komoditas_id')
|
||||
->whereNotNull('jumlah_panen')
|
||||
->selectRaw("COALESCE(k.nama, 'Belum ditentukan') as label")
|
||||
->selectRaw("SUM({$expr}) as total")
|
||||
->groupBy('k.id', 'k.nama')
|
||||
->get()
|
||||
->filter(fn ($row) => round((float) $row->total, 2) > 0)
|
||||
->sortByDesc('total')
|
||||
->values();
|
||||
|
||||
return [
|
||||
'labels' => $rows->pluck('label')->values()->all(),
|
||||
'series' => $rows->pluck('total')->map(fn ($value) => round((float) $value, 2))->values()->all(),
|
||||
];
|
||||
}
|
||||
|
||||
private function produktivitasPerKomoditasChart(array $filters): array
|
||||
{
|
||||
$expr = $this->kgCaseExpr('produksi.jumlah_panen', 'produksi.satuan');
|
||||
|
||||
$rows = $this->applyProduksiFilters(Produksi::query(), $filters)
|
||||
->leftJoin('master_komoditas as k', 'k.id', '=', 'produksi.komoditas_id')
|
||||
->selectRaw("COALESCE(k.nama, 'Belum ditentukan') as label")
|
||||
->selectRaw('AVG(produksi.produktivitas) FILTER (WHERE produksi.produktivitas > 0) as avg_prod')
|
||||
->selectRaw("SUM({$expr}) as total_kg")
|
||||
->selectRaw('SUM(produksi.luas_panen) as total_luas')
|
||||
->groupBy('k.id', 'k.nama')
|
||||
->get()
|
||||
->map(function ($row) {
|
||||
$avgProd = (float) $row->avg_prod;
|
||||
$totalLuas = (float) $row->total_luas;
|
||||
$totalKg = (float) $row->total_kg;
|
||||
|
||||
$value = $avgProd > 0
|
||||
? $avgProd
|
||||
: ($totalLuas > 0 ? $totalKg / $totalLuas : 0);
|
||||
|
||||
return (object) ['label' => $row->label, 'total' => round($value, 2)];
|
||||
})
|
||||
->filter(fn ($row) => $row->total > 0)
|
||||
->sortByDesc('total')
|
||||
->values();
|
||||
|
||||
return [
|
||||
'labels' => $rows->pluck('label')->values()->all(),
|
||||
'series' => $rows->pluck('total')->values()->all(),
|
||||
];
|
||||
}
|
||||
|
||||
private function luasPanenPerKomoditasChart(array $filters): array
|
||||
{
|
||||
$rows = $this->applyProduksiFilters(Produksi::query(), $filters)
|
||||
->leftJoin('master_komoditas as k', 'k.id', '=', 'produksi.komoditas_id')
|
||||
->whereNotNull('luas_panen')
|
||||
->selectRaw("COALESCE(k.nama, 'Belum ditentukan') as label")
|
||||
->selectRaw('SUM(produksi.luas_panen) as total')
|
||||
->groupBy('k.id', 'k.nama')
|
||||
->havingRaw('SUM(produksi.luas_panen) > 0')
|
||||
->orderByDesc('total')
|
||||
->get();
|
||||
|
||||
return [
|
||||
'labels' => $rows->pluck('label')->values()->all(),
|
||||
'series' => $rows->pluck('total')->map(fn ($value) => round((float) $value, 2))->values()->all(),
|
||||
];
|
||||
}
|
||||
|
||||
private function statusPanenChart(array $filters): array
|
||||
{
|
||||
$rows = $this->applyProduksiFilters(Produksi::query(), $filters)
|
||||
->whereNotNull('status_panen')
|
||||
->selectRaw("COALESCE(status_panen, 'Belum ditentukan') as label")
|
||||
->selectRaw('COUNT(produksi.id) as total')
|
||||
->groupBy('status_panen')
|
||||
->orderByDesc('total')
|
||||
->get();
|
||||
|
||||
return $this->chartFromRows($rows, fn ($label) => $this->shortEnumLabel((string) $label));
|
||||
}
|
||||
|
||||
private function surveyProduktivitasByUnitChart(array $filters, string $unit): array
|
||||
{
|
||||
$rows = $this->applySurveyProduktivitasFilters(SurveyProduktivitas::query(), $filters)
|
||||
->leftJoin('master_komoditas as k', 'k.id', '=', 'survey_produktivitas.komoditas_id')
|
||||
->where('satuan', $unit)
|
||||
->whereNotNull('kuantitas')
|
||||
->selectRaw("COALESCE(k.nama, 'Belum ditentukan') as label")
|
||||
->selectRaw('AVG(survey_produktivitas.kuantitas) as total')
|
||||
->groupBy('k.id', 'k.nama')
|
||||
->havingRaw('AVG(survey_produktivitas.kuantitas) > 0')
|
||||
->orderByDesc('total')
|
||||
->get();
|
||||
|
||||
return [
|
||||
'labels' => $rows->pluck('label')->values()->all(),
|
||||
'series' => $rows->pluck('total')->map(fn ($value) => round((float) $value, 2))->values()->all(),
|
||||
];
|
||||
}
|
||||
|
||||
private function chartFromRows($rows, callable $labelResolver): array
|
||||
{
|
||||
return [
|
||||
'labels' => $rows
|
||||
->pluck('label')
|
||||
->map(fn ($label) => $labelResolver($label))
|
||||
->values()
|
||||
->all(),
|
||||
'series' => $rows
|
||||
->pluck('total')
|
||||
->map(fn ($value) => (int) $value)
|
||||
->values()
|
||||
->all(),
|
||||
];
|
||||
}
|
||||
|
||||
private function tanamanAttributeChart(array $filters, string $field, callable $labelResolver): array
|
||||
{
|
||||
$qualifiedField = 'map_tanaman.'.$field;
|
||||
|
||||
$rows = $this->applyTanamanTableFilters(DB::table('map_tanaman'), $filters)
|
||||
->whereNull('map_tanaman.deleted_at')
|
||||
->whereNotNull($qualifiedField)
|
||||
->selectRaw($qualifiedField.' as label')
|
||||
->selectRaw('COUNT(map_tanaman.id) as total')
|
||||
->groupBy($qualifiedField)
|
||||
->orderByDesc('total')
|
||||
->get();
|
||||
|
||||
if ($rows->isEmpty()) {
|
||||
$rows = $this->applyLahanFilters(Lahan::query(), $filters)
|
||||
->whereNotNull($field)
|
||||
->selectRaw($field.' as label')
|
||||
->selectRaw('COUNT(map_lahan.id) as total')
|
||||
->groupBy($field)
|
||||
->orderByDesc('total')
|
||||
->get();
|
||||
}
|
||||
|
||||
return $this->chartFromRows($rows, $labelResolver);
|
||||
}
|
||||
|
||||
private function applyPetaniFilters($query, array $filters)
|
||||
{
|
||||
return $query
|
||||
->when($filters['kecamatan'] !== 'all', function ($query) use ($filters) {
|
||||
$query->whereHas('desaKelurahan', fn ($desa) => $desa->where('kecamatan_id', $filters['kecamatan']));
|
||||
})
|
||||
->when($filters['desa'] !== 'all', fn ($query) => $query->where('desa_kelurahan_id', $filters['desa']))
|
||||
->when($filters['komoditas'] !== 'all', fn ($query) => $query->where('komoditas_id', $filters['komoditas']))
|
||||
->when($filters['segmentasi'] !== 'all', function ($query) use ($filters) {
|
||||
$query->whereHas('skor', function ($skor) use ($filters) {
|
||||
$this->applySegmentasiRange($skor, $filters['segmentasi']);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private function applyLahanFilters($query, array $filters)
|
||||
{
|
||||
return $query
|
||||
->when($filters['kecamatan'] !== 'all', function ($query) use ($filters) {
|
||||
$query->whereHas('desaKelurahan', fn ($desa) => $desa->where('kecamatan_id', $filters['kecamatan']));
|
||||
})
|
||||
->when($filters['desa'] !== 'all', fn ($query) => $query->where('desa_kelurahan_id', $filters['desa']))
|
||||
->when($filters['komoditas'] !== 'all', fn ($query) => $query->where('komoditas_id', $filters['komoditas']))
|
||||
->when($filters['segmentasi'] !== 'all', function ($query) use ($filters) {
|
||||
$query->whereIn('petani_id', $this->segmentasiPetaniIdsQuery($filters['segmentasi']));
|
||||
});
|
||||
}
|
||||
|
||||
private function applyProduksiFilters($query, array $filters)
|
||||
{
|
||||
return $query
|
||||
->when($filters['kecamatan'] !== 'all' || $filters['desa'] !== 'all' || $filters['segmentasi'] !== 'all', function ($query) use ($filters) {
|
||||
$query->whereHas('lahan', function ($lahan) use ($filters) {
|
||||
$this->applyLahanFilters($lahan, $filters);
|
||||
});
|
||||
})
|
||||
->when($filters['komoditas'] !== 'all', fn ($query) => $query->where('komoditas_id', $filters['komoditas']));
|
||||
}
|
||||
|
||||
private function applySosialEkonomiFilters($query, array $filters)
|
||||
{
|
||||
return $query->whereHas('petani', function ($petani) use ($filters) {
|
||||
$this->applyPetaniFilters($petani, $filters);
|
||||
});
|
||||
}
|
||||
|
||||
private function applyPraktikBudidayaFilters($query, array $filters)
|
||||
{
|
||||
return $query->whereHas('petani', function ($petani) use ($filters) {
|
||||
$this->applyPetaniFilters($petani, $filters);
|
||||
});
|
||||
}
|
||||
|
||||
private function applySurveyProduktivitasFilters($query, array $filters)
|
||||
{
|
||||
return $query
|
||||
->whereHas('petani', function ($petani) use ($filters) {
|
||||
$this->applyPetaniFilters($petani, $filters);
|
||||
})
|
||||
->when($filters['komoditas'] !== 'all', fn ($query) => $query->where('komoditas_id', $filters['komoditas']));
|
||||
}
|
||||
|
||||
private function applyTanamanTableFilters($query, array $filters)
|
||||
{
|
||||
return $query
|
||||
->leftJoin('map_lahan as l', 'l.id', '=', 'map_tanaman.lahan_id')
|
||||
->when($filters['kecamatan'] !== 'all', function ($query) use ($filters) {
|
||||
$query
|
||||
->leftJoin('master_desa_kelurahan as d', 'd.id', '=', 'l.desa_kelurahan_id')
|
||||
->where('d.kecamatan_id', $filters['kecamatan']);
|
||||
})
|
||||
->when($filters['desa'] !== 'all', fn ($query) => $query->where('l.desa_kelurahan_id', $filters['desa']))
|
||||
->when($filters['komoditas'] !== 'all', fn ($query) => $query->where('map_tanaman.komoditas_id', $filters['komoditas']))
|
||||
->when($filters['segmentasi'] !== 'all', function ($query) use ($filters) {
|
||||
$query->where(function ($query) use ($filters) {
|
||||
$query
|
||||
->whereIn('map_tanaman.petani_id', $this->segmentasiPetaniIdsQuery($filters['segmentasi']))
|
||||
->orWhereIn('l.petani_id', $this->segmentasiPetaniIdsQuery($filters['segmentasi']));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private function segmentasiPetaniIdsQuery(string $segmentasi)
|
||||
{
|
||||
return Petani::query()
|
||||
->select('id')
|
||||
->whereHas('skor', function ($skor) use ($segmentasi) {
|
||||
$this->applySegmentasiRange($skor, $segmentasi);
|
||||
});
|
||||
}
|
||||
|
||||
private function applySegmentasiRange($query, string $segmentasi)
|
||||
{
|
||||
return match ($segmentasi) {
|
||||
'Penggerak' => $query->where('skor_kesiapan_adopsi', '>=', 4),
|
||||
'Perintis' => $query->where('skor_kesiapan_adopsi', '>=', 3)->where('skor_kesiapan_adopsi', '<', 4),
|
||||
'Pemerhati' => $query->where('skor_kesiapan_adopsi', '>=', 2)->where('skor_kesiapan_adopsi', '<', 3),
|
||||
'Pemula' => $query->where('skor_kesiapan_adopsi', '>=', 1)->where('skor_kesiapan_adopsi', '<', 2),
|
||||
default => $query->where(function ($query) {
|
||||
$query->whereNull('skor_kesiapan_adopsi')->orWhere('skor_kesiapan_adopsi', '<', 1);
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* SQL CASE expression converting a jumlah/satuan pair to kilograms,
|
||||
* so aggregation happens in the database instead of pulling every
|
||||
* row of `produksi` into PHP to sum with toKilograms().
|
||||
*/
|
||||
private function kgCaseExpr(string $jumlahColumn, string $satuanColumn): string
|
||||
{
|
||||
return "CASE LOWER({$satuanColumn})
|
||||
WHEN 'ton' THEN {$jumlahColumn} * 1000
|
||||
WHEN 'kuintal' THEN {$jumlahColumn} * 100
|
||||
WHEN 'kwintal' THEN {$jumlahColumn} * 100
|
||||
ELSE {$jumlahColumn}
|
||||
END";
|
||||
}
|
||||
|
||||
private function shortEnumLabel(string $value): string
|
||||
{
|
||||
return str($value)
|
||||
->replace('_', ' ')
|
||||
->headline()
|
||||
->toString();
|
||||
}
|
||||
|
||||
private function enumLabel(string $enumClass, int|string $value): string
|
||||
{
|
||||
$case = $enumClass::tryFrom($value);
|
||||
|
||||
if (! $case) {
|
||||
return $this->shortEnumLabel((string) $value);
|
||||
}
|
||||
|
||||
return method_exists($case, 'label')
|
||||
? $case->label()
|
||||
: $this->shortEnumLabel((string) $case->value);
|
||||
}
|
||||
|
||||
private function keparahanSeranganLabel(string $value): string
|
||||
{
|
||||
return match (KeparahanSerangan::tryFrom($value)) {
|
||||
KeparahanSerangan::RINGAN => 'Ringan',
|
||||
KeparahanSerangan::SEDANG => 'Sedang',
|
||||
KeparahanSerangan::BERAT => 'Parah',
|
||||
default => $this->shortEnumLabel($value),
|
||||
};
|
||||
}
|
||||
|
||||
private function pemberianPupukLabel(int $value): string
|
||||
{
|
||||
return match (PemberianPupuk::tryFrom($value)) {
|
||||
PemberianPupuk::TIDAK_PERNAH => 'Tidak pernah',
|
||||
PemberianPupuk::JENIS_DOSIS_WAKTU_TIDAK_MENENTU => 'Tidak menentu',
|
||||
PemberianPupuk::JENIS_SESUAI_DOSIS_TIDAK_MENENTU => 'Jenis sesuai',
|
||||
PemberianPupuk::JENIS_DOSIS_SESUAI_WAKTU_CUKUP_TEPAT => 'Cukup tepat',
|
||||
PemberianPupuk::JENIS_DOSIS_SESUAI_WAKTU_TEPAT => 'Tepat & rutin',
|
||||
default => $this->shortEnumLabel((string) $value),
|
||||
};
|
||||
}
|
||||
|
||||
private function toKilograms(float $value, string $unit): float
|
||||
{
|
||||
return match (strtolower($unit)) {
|
||||
'ton' => $value * 1000,
|
||||
'kuintal', 'kwintal' => $value * 100,
|
||||
default => $value,
|
||||
};
|
||||
}
|
||||
|
||||
private function withAllOption(string $label, array $options): array
|
||||
{
|
||||
return array_merge([
|
||||
[
|
||||
'value' => 'all',
|
||||
'label' => $label,
|
||||
],
|
||||
], $options);
|
||||
}
|
||||
|
||||
private function kabupatenLabel(KabupatenKota $kabupaten): string
|
||||
{
|
||||
return preg_replace('/^(kabupaten|kota)\s+/i', '', $kabupaten->nama);
|
||||
}
|
||||
|
||||
private function segmentLabel(float $score): string
|
||||
{
|
||||
return match (true) {
|
||||
$score >= 4.0 => 'Penggerak',
|
||||
$score >= 3.0 => 'Perintis',
|
||||
$score >= 2.0 => 'Pemerhati',
|
||||
$score >= 1.0 => 'Pemula',
|
||||
default => 'Belum tersegmentasi',
|
||||
};
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -17,15 +17,18 @@ class ProfilPetaniController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
if (!Gate::allows('super_admin') && !Gate::allows('admin')) {
|
||||
if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) {
|
||||
abort(403);
|
||||
}
|
||||
$title = 'Dashboard Profil Petani';
|
||||
|
||||
$komoditas = Komoditas::all();
|
||||
$komoditas = Komoditas::select('id', 'nama')->where('is_utama', true)->get();
|
||||
$komoditasIds = $komoditas->pluck('id');
|
||||
|
||||
$petani = Petani::pluck('id');
|
||||
$lahan = Lahan::whereIn('petani_id', $petani)->get();
|
||||
$petaniIds = fn () => Petani::query()->select('id');
|
||||
$lahanIds = fn () => Lahan::query()
|
||||
->whereIn('petani_id', $petaniIds())
|
||||
->select('id');
|
||||
|
||||
$petaniSummary = Petani::select(
|
||||
'komoditas_id',
|
||||
@@ -35,17 +38,17 @@ class ProfilPetaniController extends Controller
|
||||
->get();
|
||||
$lahanSummary = Lahan::select(
|
||||
'komoditas_id',
|
||||
DB::raw('COUNT(*) as total',
|
||||
DB::raw('SUM(luas_lahan) as luas_lahan'))
|
||||
DB::raw('COUNT(*) as total'),
|
||||
DB::raw('SUM(luas_lahan) as luas_lahan')
|
||||
)
|
||||
->whereIn('petani_id', $petani)
|
||||
->whereIn('petani_id', $petaniIds())
|
||||
->groupBy('komoditas_id')
|
||||
->get();
|
||||
$tanamanSummary = Tanaman::select(
|
||||
'komoditas_id',
|
||||
DB::raw('COUNT(*) as total')
|
||||
)
|
||||
->whereIn('lahan_id', $lahan->pluck('id'))
|
||||
->whereIn('lahan_id', $lahanIds())
|
||||
->groupBy('komoditas_id')
|
||||
->get();
|
||||
|
||||
@@ -59,7 +62,7 @@ class ProfilPetaniController extends Controller
|
||||
END
|
||||
";
|
||||
$skorSummary = SkorPetani::selectRaw("$case AS segmen, COUNT(*) AS total")
|
||||
->whereIn('petani_id', $petani)
|
||||
->whereIn('petani_id', $petaniIds())
|
||||
->groupByRaw($case)
|
||||
->get();
|
||||
|
||||
@@ -70,18 +73,101 @@ class ProfilPetaniController extends Controller
|
||||
$case AS segmen,
|
||||
COUNT(petani.id) AS total
|
||||
")
|
||||
->whereIn('petani_skor.petani_id', $petani)
|
||||
->whereIn('petani_skor.petani_id', $petaniIds())
|
||||
->groupBy('petani.komoditas_id')
|
||||
->groupByRaw($case)
|
||||
->get();
|
||||
|
||||
$skorAverage = SkorPetani::query()
|
||||
->join('petani', 'petani_skor.petani_id', '=', 'petani.id')
|
||||
->whereIn('petani_id', $petaniIds())
|
||||
->whereIn('petani.komoditas_id', $komoditasIds)
|
||||
->avg('skor_kesiapan_adopsi');
|
||||
if ($skorAverage >= 4.0) {
|
||||
$segmenAverage = 'Penggerak';
|
||||
} elseif ($skorAverage >= 3.0) {
|
||||
$segmenAverage = 'Perintis';
|
||||
} elseif ($skorAverage >= 2.0) {
|
||||
$segmenAverage = 'Pemerhati';
|
||||
} elseif ($skorAverage >= 1.0) {
|
||||
$segmenAverage = 'Pemula';
|
||||
} else {
|
||||
$segmenAverage = 'Belum Lengkap';
|
||||
}
|
||||
|
||||
$skorDimensi = SkorPetani::query()
|
||||
->join('petani', 'petani_skor.petani_id', '=', 'petani.id')
|
||||
->whereIn('petani_id', $petaniIds())
|
||||
->whereIn('petani.komoditas_id', $komoditasIds)
|
||||
->selectRaw("
|
||||
avg(skor_d2_sistem_praktik_budidaya) as skor_d2_sistem_praktik_budidaya,
|
||||
avg(skor_d4_sosial_ekonomi) as skor_d4_sosial_ekonomi,
|
||||
avg(skor_d6_rantai_nilai_akses_pasar) as skor_d6_rantai_nilai_akses_pasar")
|
||||
->first();
|
||||
|
||||
$skorKomponen = SkorPetani::query()
|
||||
->join('petani', 'petani_skor.petani_id', '=', 'petani.id')
|
||||
->whereIn('petani_id', $petaniIds())
|
||||
->whereIn('petani.komoditas_id', $komoditasIds)
|
||||
->selectRaw("avg(skor_komp_1_d4_k3_motivasi_kapasitas_adopsi) as skor_komp_1_d4_k3_motivasi_kapasitas_adopsi,
|
||||
avg(skor_komp_2_d4_k2_ekonomi_rumah_tangga) as skor_komp_2_d4_k2_ekonomi_rumah_tangga,
|
||||
avg(skor_komp_3_d2_praktik_budidaya_gap) as skor_komp_3_d2_praktik_budidaya_gap,
|
||||
avg(skor_komp_4_d4_k4_keterlibatan_kelembagaan) as skor_komp_4_d4_k4_keterlibatan_kelembagaan,
|
||||
avg(skor_komp_5_d4_k5_k6_akses_kebebasan_keuangan) as skor_komp_5_d4_k5_k6_akses_kebebasan_keuangan,
|
||||
avg(skor_komp_6_d6_k1_k4_k5_rantai_nilai_akses_pasar) as skor_komp_6_d6_k1_k4_k5_rantai_nilai_akses_pasar")
|
||||
->first();
|
||||
|
||||
$skorMultiDimensi = SkorPetani::query()
|
||||
->join('petani', 'petani_skor.petani_id', '=', 'petani.id')
|
||||
->whereIn('petani_id', $petaniIds())
|
||||
->whereIn('petani.komoditas_id', $komoditasIds)
|
||||
->selectRaw("$case AS segmen,
|
||||
avg(skor_kesiapan_adopsi) as skor_kesiapan_adopsi,
|
||||
avg(skor_d2_sistem_praktik_budidaya) as skor_d2_sistem_praktik_budidaya,
|
||||
avg(skor_d4_sosial_ekonomi) as skor_d4_sosial_ekonomi,
|
||||
avg(skor_d6_rantai_nilai_akses_pasar) as skor_d6_rantai_nilai_akses_pasar")
|
||||
->groupByRaw($case)
|
||||
->get();
|
||||
|
||||
$skorMultiKomponen = SkorPetani::query()
|
||||
->join('petani', 'petani_skor.petani_id', '=', 'petani.id')
|
||||
->whereIn('petani_id', $petaniIds())
|
||||
->whereIn('petani.komoditas_id', $komoditasIds)
|
||||
->selectRaw("$case AS segmen,
|
||||
avg(skor_kesiapan_adopsi) as skor_kesiapan_adopsi,
|
||||
avg(skor_komp_1_d4_k3_motivasi_kapasitas_adopsi) as skor_komp_1_d4_k3_motivasi_kapasitas_adopsi,
|
||||
avg(skor_komp_2_d4_k2_ekonomi_rumah_tangga) as skor_komp_2_d4_k2_ekonomi_rumah_tangga,
|
||||
avg(skor_komp_3_d2_praktik_budidaya_gap) as skor_komp_3_d2_praktik_budidaya_gap,
|
||||
avg(skor_komp_4_d4_k4_keterlibatan_kelembagaan) as skor_komp_4_d4_k4_keterlibatan_kelembagaan,
|
||||
avg(skor_komp_5_d4_k5_k6_akses_kebebasan_keuangan) as skor_komp_5_d4_k5_k6_akses_kebebasan_keuangan,
|
||||
avg(skor_komp_6_d6_k1_k4_k5_rantai_nilai_akses_pasar) as skor_komp_6_d6_k1_k4_k5_rantai_nilai_akses_pasar")
|
||||
->groupByRaw($case)
|
||||
->get();
|
||||
|
||||
$petani = Petani::query()
|
||||
->select('id', 'nama', 'jenis_kelamin', 'tanggal_lahir', 'komoditas_id')
|
||||
->with([
|
||||
'komoditas:id,nama',
|
||||
'skor:id,petani_id,skor_kesiapan_adopsi,skor_d2_sistem_praktik_budidaya,skor_d4_sosial_ekonomi,skor_d6_rantai_nilai_akses_pasar,skor_komp_1_d4_k3_motivasi_kapasitas_adopsi,skor_komp_2_d4_k2_ekonomi_rumah_tangga,skor_komp_3_d2_praktik_budidaya_gap,skor_komp_4_d4_k4_keterlibatan_kelembagaan,skor_komp_5_d4_k5_k6_akses_kebebasan_keuangan,skor_komp_6_d6_k1_k4_k5_rantai_nilai_akses_pasar',
|
||||
])
|
||||
->whereIn('komoditas_id', $komoditasIds)
|
||||
->orderBy('nama')
|
||||
->get();
|
||||
|
||||
return view('dashboard.petani', compact('title',
|
||||
'komoditas',
|
||||
'petaniSummary',
|
||||
'lahanSummary',
|
||||
'tanamanSummary',
|
||||
'skorSummary',
|
||||
'skorKomoditasSummary'
|
||||
'skorKomoditasSummary',
|
||||
'segmenAverage',
|
||||
'skorAverage',
|
||||
'skorDimensi',
|
||||
'skorKomponen',
|
||||
'skorMultiDimensi',
|
||||
'skorMultiKomponen',
|
||||
'petani',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Web\Dashboard;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
@@ -11,8 +12,12 @@ use App\Models\User;
|
||||
use App\Models\Pertanian\Petani;
|
||||
use App\Models\Pertanian\Komoditas;
|
||||
use App\Models\Pertanian\Lahan;
|
||||
use App\Models\Pertanian\Tanaman;
|
||||
use App\Models\Pertanian\SurveyPelatihan;
|
||||
use App\Models\Pertanian\SurveySertifikasi;
|
||||
use App\Enums\AksesPasar\PraktikFermentasi;
|
||||
use App\Enums\AksesPasar\SaluranTerdahulu;
|
||||
use App\Enums\AksesPasar\SkorPengalaman;
|
||||
|
||||
use BackedEnum;
|
||||
|
||||
@@ -57,11 +62,13 @@ class SurveyPetaniController extends Controller
|
||||
->leftJoin("{$komoditasTable} as k", 'k.id', '=', 'p.komoditas_id')
|
||||
->selectRaw("COALESCE(k.nama, 'Belum ditentukan') as nama")
|
||||
->selectRaw('COUNT(p.id) as petani_count')
|
||||
->where('k.is_utama', true)
|
||||
->groupBy('k.id', 'k.nama')
|
||||
->orderByDesc('petani_count')
|
||||
->get();
|
||||
|
||||
$totalPetani = (int) $komoditasCollection->sum('petani_count');
|
||||
// $totalPetani = (int) $komoditasCollection->sum('petani_count');
|
||||
$totalPetani = Petani::count();
|
||||
$circumference = 2 * pi() * 46;
|
||||
|
||||
$komoditas = $komoditasCollection
|
||||
@@ -89,36 +96,34 @@ class SurveyPetaniController extends Controller
|
||||
// Progres pengisian data per petani
|
||||
|
||||
$progressPetani = Petani::query()
|
||||
->select([
|
||||
'id',
|
||||
'nama',
|
||||
])
|
||||
->withExists([
|
||||
'lahans as has_lahan',
|
||||
'praktikBudidaya as has_praktik_budidaya',
|
||||
'sosialEkonomi as has_sosial_ekonomi',
|
||||
'aksesPasar as has_akses_pasar',
|
||||
->select(['id', 'nama', 'komoditas_id'])
|
||||
->with([
|
||||
'komoditas:id,nama',
|
||||
'lahans',
|
||||
'praktikBudidaya',
|
||||
'sosialEkonomi',
|
||||
'aksesPasar',
|
||||
])
|
||||
->orderBy('nama')
|
||||
->get()
|
||||
->map(function ($petani) {
|
||||
$status = [
|
||||
'lahan' => [
|
||||
'label' => 'Lahan',
|
||||
'done' => (bool) $petani->has_lahan,
|
||||
],
|
||||
'praktik_budidaya' => [
|
||||
'label' => 'Praktik Budidaya',
|
||||
'done' => (bool) $petani->has_praktik_budidaya,
|
||||
],
|
||||
'sosial_ekonomi' => [
|
||||
'label' => 'Sosial Ekonomi',
|
||||
'done' => (bool) $petani->has_sosial_ekonomi,
|
||||
],
|
||||
'akses_pasar' => [
|
||||
'label' => 'Akses Pasar',
|
||||
'done' => (bool) $petani->has_akses_pasar,
|
||||
],
|
||||
'lahan' => $this->moduleStatus(
|
||||
'Lahan',
|
||||
$this->hasCompleteLahan($petani)
|
||||
),
|
||||
'praktik_budidaya' => $this->moduleStatus(
|
||||
'Praktik Budidaya',
|
||||
$this->hasCompletePraktikBudidaya($petani->praktikBudidaya)
|
||||
),
|
||||
'sosial_ekonomi' => $this->moduleStatus(
|
||||
'Sosial Ekonomi',
|
||||
$this->hasCompleteSosialEkonomi($petani->sosialEkonomi)
|
||||
),
|
||||
'akses_pasar' => $this->moduleStatus(
|
||||
'Akses Pasar',
|
||||
$this->hasCompleteAksesPasar($petani->aksesPasar)
|
||||
),
|
||||
];
|
||||
|
||||
$totalModul = count($status);
|
||||
@@ -128,12 +133,13 @@ class SurveyPetaniController extends Controller
|
||||
->count();
|
||||
|
||||
return [
|
||||
'id' => $petani->id,
|
||||
'name' => $petani->nama,
|
||||
'status' => $status,
|
||||
'done' => $modulSelesai,
|
||||
'total' => $totalModul,
|
||||
'percent' => $totalModul > 0
|
||||
'id' => $petani->id,
|
||||
'name' => $petani->nama,
|
||||
'komoditas' => $petani->komoditas?->nama ?? 'Belum ditentukan',
|
||||
'status' => $status,
|
||||
'done' => $modulSelesai,
|
||||
'total' => $totalModul,
|
||||
'percent' => $totalModul > 0
|
||||
? (int) round(($modulSelesai / $totalModul) * 100)
|
||||
: 0,
|
||||
];
|
||||
@@ -142,23 +148,30 @@ class SurveyPetaniController extends Controller
|
||||
|
||||
// Ringkasan lahan
|
||||
|
||||
$totalLuasLahan = (float) Lahan::query()
|
||||
->sum('luas_lahan');
|
||||
|
||||
$totalJumlahTanaman = (int) Lahan::query()
|
||||
->sum('jumlah_tanaman');
|
||||
// $totalLuasLahan = ((float) Lahan::query()
|
||||
// ->sum('luas_lahan')) / 10000;
|
||||
$totalLuasLahan = Lahan::has('petani')->sum('luas_lahan');
|
||||
|
||||
// Jumlah tanaman berdasarkan komoditas petani
|
||||
|
||||
$tanamanPerKomoditasCollection = DB::table("{$lahanTable} as l")
|
||||
$tanamanTable = (new Tanaman())->getTable();
|
||||
|
||||
$tanamanPerKomoditasCollection = DB::table("{$tanamanTable} as t")
|
||||
->join("{$lahanTable} as l", 'l.id', '=', 't.lahan_id')
|
||||
->join("{$petaniTable} as p", 'p.id', '=', 'l.petani_id')
|
||||
->leftJoin("{$komoditasTable} as k", 'k.id', '=', 'p.komoditas_id')
|
||||
->leftJoin("{$komoditasTable} as k", 'k.id', '=', 't.komoditas_id')
|
||||
->selectRaw("COALESCE(k.nama, 'Belum ditentukan') as label")
|
||||
->selectRaw('COALESCE(SUM(l.jumlah_tanaman), 0) as total')
|
||||
->selectRaw('COUNT(t.id) as total')
|
||||
->where('k.is_utama', true)
|
||||
->whereNull('t.deleted_at')
|
||||
->whereNull('l.deleted_at')
|
||||
->whereNull('p.deleted_at')
|
||||
->groupBy('k.id', 'k.nama')
|
||||
->orderByDesc('total')
|
||||
->get();
|
||||
|
||||
$totalJumlahTanaman = (int) $tanamanPerKomoditasCollection->sum('total');
|
||||
|
||||
$tanamanPerKomoditas = $tanamanPerKomoditasCollection
|
||||
->map(function ($item, $index) use ($colors, $totalJumlahTanaman) {
|
||||
$color = $colors[$index % count($colors)];
|
||||
@@ -268,6 +281,147 @@ class SurveyPetaniController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
private function moduleStatus(string $label, bool $done): array
|
||||
{
|
||||
return [
|
||||
'label' => $label,
|
||||
'done' => $done,
|
||||
];
|
||||
}
|
||||
|
||||
private function hasCompleteLahan(Petani $petani): bool
|
||||
{
|
||||
return $petani->lahans->contains(function ($lahan) {
|
||||
return $this->allFieldsFilled($lahan, [
|
||||
'luas_lahan',
|
||||
'komoditas_id',
|
||||
'jenis_komoditas_id',
|
||||
'umur_tanaman',
|
||||
'kondisi_tanaman',
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
private function hasCompletePraktikBudidaya(?Model $praktikBudidaya): bool
|
||||
{
|
||||
return $this->allFieldsFilled($praktikBudidaya, [
|
||||
'frekuensi_pemangkasan_utama',
|
||||
'pemberian_pupuk',
|
||||
'metode_penanganan_opt',
|
||||
'frekuensi_sanitasi_lahan',
|
||||
'penanganan_gulma',
|
||||
'kondisi_pengelolaan_air',
|
||||
]);
|
||||
}
|
||||
|
||||
private function hasCompleteSosialEkonomi(?Model $sosialEkonomi): bool
|
||||
{
|
||||
return $this->allFieldsFilled($sosialEkonomi, [
|
||||
'skor_periode_paceklik',
|
||||
'skor_kemampuan_menabung',
|
||||
'skor_kecukupan_penghasilan',
|
||||
'skor_kejelasan_status_lahan',
|
||||
'kemauan_mencoba_praktik_baru',
|
||||
'pemahaman_mutu_harga',
|
||||
'panutan_petani_sukses',
|
||||
'kemauan_ikut_program',
|
||||
'orientasi_rencana_jangka_panjang',
|
||||
'keaktifan_pertemuan_kelompok',
|
||||
'peran_dalam_kelompok',
|
||||
'manfaat_kelompok_tani',
|
||||
'status_rekening_tabungan',
|
||||
'pengalaman_kredit_kebun',
|
||||
'kondisi_aset_produktif',
|
||||
'keterlibatan_program_keuangan_formal',
|
||||
'keterikatan_hutang_panen',
|
||||
'praktik_ijon',
|
||||
'kebebasan_memilih_pembeli',
|
||||
]);
|
||||
}
|
||||
|
||||
private function hasCompleteAksesPasar(?Model $aksesPasar): bool
|
||||
{
|
||||
if (! $this->allFieldsFilled($aksesPasar, [
|
||||
'skor_pengalaman',
|
||||
'praktik_fermentasi',
|
||||
'skor_kesadaran_premium',
|
||||
'tingkat_kepercayaan_pembeli_baru',
|
||||
])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$skorPengalaman = (int) $aksesPasar->getAttribute('skor_pengalaman');
|
||||
|
||||
if (
|
||||
$skorPengalaman !== SkorPengalaman::BELUM_PERNAH->value
|
||||
&& ! $this->valueIsFilled($aksesPasar->getAttribute('saluran_terdahulu'))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
in_array($skorPengalaman, [
|
||||
SkorPengalaman::PERNAH_BERHENTI_TIDAK_ADA_AKSES->value,
|
||||
SkorPengalaman::PERNAH_BERHENTI_MUNGKIN_KEMBALI->value,
|
||||
], true)
|
||||
&& ! $this->valueIsFilled($aksesPasar->getAttribute('alasan_berhenti'))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
in_array(
|
||||
SaluranTerdahulu::LAINNYA->value,
|
||||
$aksesPasar->getAttribute('saluran_terdahulu') ?? [],
|
||||
true
|
||||
)
|
||||
&& ! $this->valueIsFilled($aksesPasar->getAttribute('saluran_terdahulu_lainnya'))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
$aksesPasar->getAttribute('praktik_fermentasi') !== PraktikFermentasi::BELUM_PERNAH->value
|
||||
&& ! $this->allFieldsFilled($aksesPasar, [
|
||||
'proses_fermentasi',
|
||||
'model_fermentasi_memungkinkan',
|
||||
])
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function allFieldsFilled(?Model $model, array $fields): bool
|
||||
{
|
||||
if (! $model) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($fields as $field) {
|
||||
if (! $this->valueIsFilled($model->getAttribute($field))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function valueIsFilled(mixed $value): bool
|
||||
{
|
||||
if ($value instanceof BackedEnum) {
|
||||
$value = $value->value;
|
||||
}
|
||||
|
||||
if (is_array($value)) {
|
||||
return collect($value)
|
||||
->contains(fn ($item) => $this->valueIsFilled($item));
|
||||
}
|
||||
|
||||
return ! is_null($value) && $value !== '';
|
||||
}
|
||||
|
||||
private function pelatihanGapOptions(): array
|
||||
{
|
||||
$enumClass = self::GAP_ENUM_CLASS;
|
||||
@@ -335,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',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -55,19 +55,7 @@ class FormBasisDataController extends Controller
|
||||
], 404);
|
||||
}
|
||||
|
||||
$validated = $request->validate([
|
||||
'parent_id' => ['nullable', 'exists:ekosistem_basis_data_form,id'],
|
||||
'tipe' => ['required', Rule::enum(LayoutForm::class)],
|
||||
'column_width' => [
|
||||
Rule::requiredIf($request->tipe === LayoutForm::COLUMN->value),
|
||||
'nullable', 'integer', 'min:1', 'max:12'],
|
||||
'kolom_id' => [
|
||||
Rule::requiredIf($request->tipe === LayoutForm::VALUE->value),
|
||||
'nullable', 'exists:ekosistem_basis_data_kolom,id'],
|
||||
'section_label' => [
|
||||
Rule::requiredIf($request->tipe === LayoutForm::SECTION->value),
|
||||
'nullable', 'string'],
|
||||
]);
|
||||
$validated = $this->validateFormPayload($request, $tabelId);
|
||||
|
||||
FormBasisData::create([...$validated,
|
||||
'tabel_id' => $tabelId,
|
||||
@@ -115,19 +103,7 @@ class FormBasisDataController extends Controller
|
||||
], 404);
|
||||
}
|
||||
|
||||
$validated = $request->validate([
|
||||
'parent_id' => ['nullable', 'exists:ekosistem_basis_data_form,id'],
|
||||
'tipe' => ['required', Rule::enum(LayoutForm::class)],
|
||||
'column_width' => [
|
||||
Rule::requiredIf($request->tipe === LayoutForm::COLUMN->value),
|
||||
'nullable', 'integer', 'min:1', 'max:12'],
|
||||
'kolom_id' => [
|
||||
Rule::requiredIf($request->tipe === LayoutForm::VALUE->value),
|
||||
'nullable', 'exists:ekosistem_basis_data_kolom,id'],
|
||||
'section_label' => [
|
||||
Rule::requiredIf($request->tipe === LayoutForm::SECTION->value),
|
||||
'nullable', 'string'],
|
||||
]);
|
||||
$validated = $this->validateFormPayload($request, $tabelId, $form);
|
||||
|
||||
$form->update([...$validated,
|
||||
'updated_by' => auth()->user()->id
|
||||
@@ -293,4 +269,88 @@ class FormBasisDataController extends Controller
|
||||
'message' => 'Formulir basis data berhasil diperbarui.'
|
||||
], 200);
|
||||
}
|
||||
|
||||
private function validateFormPayload(Request $request, string $tabelId, ?FormBasisData $form = null): array
|
||||
{
|
||||
$tipe = $request->input('tipe');
|
||||
$needsParent = in_array($tipe, [LayoutForm::COLUMN->value, LayoutForm::VALUE->value], true);
|
||||
$mustBeRoot = in_array($tipe, [LayoutForm::ROW->value, LayoutForm::SECTION->value, LayoutForm::HR_LINE->value], true);
|
||||
|
||||
$validator = Validator::make($request->all(), [
|
||||
'parent_id' => [
|
||||
Rule::requiredIf($needsParent),
|
||||
Rule::prohibitedIf($mustBeRoot),
|
||||
'nullable',
|
||||
Rule::exists('ekosistem_basis_data_form', 'id')
|
||||
->where(fn ($query) => $query
|
||||
->where('tabel_id', $tabelId)
|
||||
->whereNull('deleted_at')),
|
||||
],
|
||||
'tipe' => ['required', Rule::enum(LayoutForm::class)],
|
||||
'column_width' => [
|
||||
Rule::requiredIf($tipe === LayoutForm::COLUMN->value),
|
||||
'nullable', 'integer', 'min:1', 'max:12'
|
||||
],
|
||||
'kolom_id' => [
|
||||
Rule::requiredIf($tipe === LayoutForm::VALUE->value),
|
||||
'nullable',
|
||||
Rule::exists('ekosistem_basis_data_kolom', 'id')
|
||||
->where(fn ($query) => $query
|
||||
->where('tabel_id', $tabelId)
|
||||
->whereNull('deleted_at')),
|
||||
],
|
||||
'section_label' => [
|
||||
Rule::requiredIf($tipe === LayoutForm::SECTION->value),
|
||||
'nullable', 'string'
|
||||
],
|
||||
]);
|
||||
|
||||
$validator->after(function ($validator) use ($request, $tabelId, $form) {
|
||||
if ($validator->errors()->isNotEmpty() || !$request->filled('parent_id')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($form && (int) $request->input('parent_id') === (int) $form->id) {
|
||||
$validator->errors()->add('parent_id', 'Induk formulir tidak boleh menunjuk ke dirinya sendiri.');
|
||||
return;
|
||||
}
|
||||
|
||||
$layout = LayoutForm::from($request->input('tipe'));
|
||||
$expectedParent = match ($layout) {
|
||||
LayoutForm::COLUMN => LayoutForm::ROW,
|
||||
LayoutForm::VALUE => LayoutForm::COLUMN,
|
||||
default => null,
|
||||
};
|
||||
|
||||
if (!$expectedParent) {
|
||||
return;
|
||||
}
|
||||
|
||||
$parent = FormBasisData::where('tabel_id', $tabelId)->find($request->input('parent_id'));
|
||||
if (!$parent || $parent->tipe !== $expectedParent) {
|
||||
$validator->errors()->add('parent_id', 'Induk formulir tidak sesuai dengan tipe formulir.');
|
||||
}
|
||||
});
|
||||
|
||||
$validated = $validator->validate();
|
||||
$layout = LayoutForm::from($validated['tipe']);
|
||||
|
||||
if (in_array($layout, [LayoutForm::ROW, LayoutForm::SECTION, LayoutForm::HR_LINE], true)) {
|
||||
$validated['parent_id'] = null;
|
||||
}
|
||||
|
||||
if ($layout !== LayoutForm::COLUMN) {
|
||||
$validated['column_width'] = null;
|
||||
}
|
||||
|
||||
if ($layout !== LayoutForm::VALUE) {
|
||||
$validated['kolom_id'] = null;
|
||||
}
|
||||
|
||||
if ($layout !== LayoutForm::SECTION) {
|
||||
$validated['section_label'] = null;
|
||||
}
|
||||
|
||||
return $validated;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,9 +28,15 @@ class AuthController extends Controller
|
||||
public function createAccount(Request $request)
|
||||
{
|
||||
$credentials = $request->validate([
|
||||
'username' => 'required|string|max:255|unique:users,username',
|
||||
'username' => 'required|string|min:6|max:255|unique:users,username',
|
||||
'email' => 'required|string|email|max:255|unique:users,email',
|
||||
'password' => 'required|string|min:8|confirmed',
|
||||
], [
|
||||
'username.unique' => 'Nama pengguna sudah digunakan.',
|
||||
'username.min' => 'Nama pengguna minimal 6 karakter.',
|
||||
'email.unique' => 'Email sudah digunakan.',
|
||||
'password.confirmed' => 'Konfirmasi password tidak cocok.',
|
||||
'password.min' => 'Password minimal 8 karakter.',
|
||||
]);
|
||||
|
||||
$user = User::create([
|
||||
@@ -89,6 +95,9 @@ class AuthController extends Controller
|
||||
$request->validate([
|
||||
'email_username' => 'required|string',
|
||||
'password' => 'required|string',
|
||||
], [
|
||||
'email_username.required' => 'Email atau username harus diisi.',
|
||||
'password.required' => 'Password harus diisi.',
|
||||
]);
|
||||
|
||||
$login = $request->input('email_username');
|
||||
|
||||
@@ -99,18 +99,20 @@ class UserController extends Controller
|
||||
public function store(Request $request)
|
||||
{
|
||||
$validator = Validator::make($request->all(), [
|
||||
'username' => ['required', 'string', 'max:255', 'unique:users,username'],
|
||||
'username' => ['required', 'string', 'max:255', 'min:6', 'unique:users,username'],
|
||||
'nama' => ['required', 'string', 'max:255'],
|
||||
'email' => ['required', 'string', 'email', 'max:255', 'unique:users,email'],
|
||||
'password' => ['required', 'string', 'min:8', 'confirmed'],
|
||||
'role' => ['required', 'string'],
|
||||
'telepon' => ['required', 'max:13'],
|
||||
'desa_kelurahan_id' => ['required'],
|
||||
'desa_kelurahan_id' => ['nullable', 'exists:desa_kelurahan,id'],
|
||||
'ktp' => ['nullable', 'min:16', 'max:16'],
|
||||
'kk' => ['nullable', 'min:16', 'max:16'],
|
||||
'file_ktp' => ['nullable', 'file', 'mimes:jpeg,jpg,png,pdf', 'max:2048'],
|
||||
'file_kk' => ['nullable', 'file', 'mimes:jpeg,jpg,pdf,png', 'max:2048'],
|
||||
], [
|
||||
'username.unique' => 'Nama pengguna sudah digunakan.',
|
||||
'username.min' => 'Nama pengguna minimal 6 karakter.',
|
||||
'password.confirmed' => 'Konfirmasi password tidak cocok!',
|
||||
'password.min' => 'Password minimal 8 karakter!',
|
||||
'email.unique' => 'Email sudah terdaftar.',
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Web\Koperasi;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\Koperasi\PengurusKoperasi;
|
||||
|
||||
class PengurusKoperasiController extends Controller
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
$query = PengurusKoperasi::query();
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$search = $request->search;
|
||||
$query->where('nama_lengkap', 'like', "%{$search}%")
|
||||
->orWhere('nama_koperasi', 'like', "%{$search}%");
|
||||
}
|
||||
|
||||
$perPage = $request->get('per_page', 10);
|
||||
$pengurus = $query->latest()->paginate($perPage);
|
||||
|
||||
$totalPengurus = PengurusKoperasi::count();
|
||||
$totalKoperasi = PengurusKoperasi::distinct('nama_koperasi')->count('nama_koperasi');
|
||||
$totalJabatan = PengurusKoperasi::distinct('posisi_dalam_koperasi')->count('posisi_dalam_koperasi');
|
||||
|
||||
return view('koperasi.profil.index', compact(
|
||||
'pengurus',
|
||||
'totalPengurus',
|
||||
'totalKoperasi',
|
||||
'totalJabatan',
|
||||
));
|
||||
}
|
||||
|
||||
public function create()
|
||||
{
|
||||
return view('koperasi.profil.add');
|
||||
}
|
||||
|
||||
public function edit($id)
|
||||
{
|
||||
$pengurus = PengurusKoperasi::findOrFail($id);
|
||||
return view('koperasi.profil.edit', compact('pengurus'));
|
||||
}
|
||||
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$validatedData = $request->validate([
|
||||
'nama_lengkap' => 'required|string|max:255',
|
||||
'posisi_dalam_koperasi' => 'required|string|max:255',
|
||||
'tahun_bergabung' => 'nullable|integer',
|
||||
'no_hp' => 'nullable|string|max:20',
|
||||
'alamat_tinggal' => 'nullable|string',
|
||||
'nama_koperasi' => 'required|string|max:255',
|
||||
'jenis_koperasi' => 'required|string|max:255',
|
||||
'jenis_koperasi_lainnya'=> 'nullable|string|max:255',
|
||||
'alamat_koperasi' => 'nullable|string',
|
||||
'skala_operasi' => 'required|string|max:255',
|
||||
'tahun_berdiri' => 'nullable|integer',
|
||||
'jumlah_anggota_awal' => 'nullable|integer',
|
||||
'jumlah_anggota_dewasa' => 'nullable|integer',
|
||||
'jumlah_anggota_anak' => 'nullable|integer',
|
||||
'jumlah_pengurus' => 'nullable|integer',
|
||||
'shu_2025' => 'nullable|numeric',
|
||||
'shu_2024' => 'nullable|numeric',
|
||||
'shu_2023' => 'nullable|numeric',
|
||||
]);
|
||||
|
||||
if ($validatedData['jenis_koperasi'] === 'Lainnya' && !empty($validatedData['jenis_koperasi_lainnya'])) {
|
||||
$validatedData['jenis_koperasi'] = $validatedData['jenis_koperasi_lainnya'];
|
||||
}
|
||||
unset($validatedData['jenis_koperasi_lainnya']);
|
||||
|
||||
$pengurus = PengurusKoperasi::findOrFail($id);
|
||||
$pengurus->update($validatedData);
|
||||
|
||||
return redirect()->route('koperasi.pengurus.index')
|
||||
->with('success', 'Profil Koperasi berhasil diperbarui!');
|
||||
}
|
||||
|
||||
public function survey(Request $request, $id)
|
||||
{
|
||||
$pengurus = PengurusKoperasi::findOrFail($id);
|
||||
$step = $request->query('step', 1);
|
||||
|
||||
if ($step == 1) {
|
||||
return view('koperasi.kelembagaan-aktor.index', compact('pengurus'));
|
||||
} elseif ($step == 2) {
|
||||
return view('koperasi.rantai-nilai-akses-pasar.index', compact('pengurus'));
|
||||
} elseif ($step == 3) {
|
||||
return view('koperasi.sosial-ekonomi.index', compact('pengurus'));
|
||||
}
|
||||
|
||||
abort(404);
|
||||
}
|
||||
|
||||
public function surveyStore(Request $request, $id)
|
||||
{
|
||||
$pengurus = PengurusKoperasi::findOrFail($id);
|
||||
$step = $request->query('step', 1);
|
||||
$data = $request->except(['_token', 'pengurus_koperasi_id']);
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
$data[$key] = json_encode($value);
|
||||
}
|
||||
}
|
||||
|
||||
$pengurus->update($data);
|
||||
|
||||
if ($step == 1) {
|
||||
if ($request->has('agregasi_komoditas') && str_contains($data['agregasi_komoditas'], 'Tidak terlibat')) {
|
||||
return redirect()->route('koperasi.pengurus.survey', ['id' => $id, 'step' => 3])
|
||||
->with('success', 'Data Kelembagaan tersimpan! (Melewati Rantai Nilai)');
|
||||
}
|
||||
|
||||
return redirect()->route('koperasi.pengurus.survey', ['id' => $id, 'step' => 2])
|
||||
->with('success', 'Data Kelembagaan berhasil disimpan!');
|
||||
}
|
||||
|
||||
if ($step == 2) {
|
||||
return redirect()->route('koperasi.pengurus.survey', ['id' => $id, 'step' => 3])
|
||||
->with('success', 'Data Rantai Nilai berhasil disimpan!');
|
||||
}
|
||||
|
||||
if ($step == 3) {
|
||||
return redirect()->route('koperasi.pengurus.index')
|
||||
->with('success', 'Seluruh data survei berhasil diselesaikan dan disimpan!');
|
||||
}
|
||||
|
||||
return redirect()->back();
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$validatedData = $request->validate([
|
||||
'nama_lengkap' => 'required|string|max:255',
|
||||
'posisi_dalam_koperasi' => 'required|string|max:255',
|
||||
'tahun_bergabung' => 'nullable|integer',
|
||||
'no_hp' => 'nullable|string|max:20',
|
||||
'alamat_tinggal' => 'nullable|string',
|
||||
'nama_koperasi' => 'required|string|max:255',
|
||||
'jenis_koperasi' => 'required|string|max:255',
|
||||
'jenis_koperasi_lainnya'=> 'nullable|string|max:255',
|
||||
'alamat_koperasi' => 'nullable|string',
|
||||
'skala_operasi' => 'required|string|max:255',
|
||||
'tahun_berdiri' => 'nullable|integer',
|
||||
'jumlah_anggota_awal' => 'nullable|integer',
|
||||
'jumlah_anggota_dewasa' => 'nullable|integer',
|
||||
'jumlah_anggota_anak' => 'nullable|integer',
|
||||
'jumlah_pengurus' => 'nullable|integer',
|
||||
'shu_2025' => 'nullable|numeric',
|
||||
'shu_2024' => 'nullable|numeric',
|
||||
'shu_2023' => 'nullable|numeric',
|
||||
]);
|
||||
|
||||
if ($validatedData['jenis_koperasi'] === 'Lainnya' && !empty($validatedData['jenis_koperasi_lainnya'])) {
|
||||
$validatedData['jenis_koperasi'] = $validatedData['jenis_koperasi_lainnya'];
|
||||
}
|
||||
unset($validatedData['jenis_koperasi_lainnya']);
|
||||
|
||||
$pengurus = PengurusKoperasi::create($validatedData);
|
||||
|
||||
return redirect()->route('koperasi.pengurus.index')
|
||||
->with('success', 'Profil Koperasi berhasil ditambahkan! Silakan klik tombol hijau untuk mulai survei.');
|
||||
}
|
||||
|
||||
public function destroy($id)
|
||||
{
|
||||
$pengurus = PengurusKoperasi::findOrFail($id);
|
||||
$pengurus->delete();
|
||||
|
||||
return redirect()->route('koperasi.pengurus.index')
|
||||
->with('success', 'Data pengurus koperasi berhasil dihapus!');
|
||||
}
|
||||
}
|
||||
@@ -51,9 +51,13 @@ class DesaKelurahanController extends Controller
|
||||
return response()->json($desa);
|
||||
}
|
||||
|
||||
public function getByKecamatan($kecamatan_id)
|
||||
public function getByKecamatan(Request $request, $kecamatan_id)
|
||||
{
|
||||
$desa = DesaKelurahan::where('kecamatan_id', $kecamatan_id)
|
||||
$query = $request->boolean('all_wilayah')
|
||||
? DesaKelurahan::withoutGlobalScope('program')
|
||||
: DesaKelurahan::query();
|
||||
|
||||
$desa = $query->where('kecamatan_id', $kecamatan_id)
|
||||
->orderBy('nama', 'asc')
|
||||
->get();
|
||||
|
||||
|
||||
@@ -51,9 +51,13 @@ class KabupatenKotaController extends Controller
|
||||
return response()->json($kabupaten);
|
||||
}
|
||||
|
||||
public function getByProvinsi($provinsi_id)
|
||||
public function getByProvinsi(Request $request, $provinsi_id)
|
||||
{
|
||||
$kabupaten = KabupatenKota::where('provinsi_id', $provinsi_id)
|
||||
$query = $request->boolean('all_wilayah')
|
||||
? KabupatenKota::withoutGlobalScope('program')
|
||||
: KabupatenKota::query();
|
||||
|
||||
$kabupaten = $query->where('provinsi_id', $provinsi_id)
|
||||
->orderBy('nama', 'asc')
|
||||
->get();
|
||||
|
||||
|
||||
@@ -51,9 +51,13 @@ class KecamatanController extends Controller
|
||||
return response()->json($kecamatan);
|
||||
}
|
||||
|
||||
public function getByKabupaten($kabupaten_id)
|
||||
public function getByKabupaten(Request $request, $kabupaten_id)
|
||||
{
|
||||
$kecamatan = Kecamatan::where('kabupaten_kota_id', $kabupaten_id)
|
||||
$query = $request->boolean('all_wilayah')
|
||||
? Kecamatan::withoutGlobalScope('program')
|
||||
: Kecamatan::query();
|
||||
|
||||
$kecamatan = $query->where('kabupaten_kota_id', $kabupaten_id)
|
||||
->orderBy('nama', 'asc')
|
||||
->get();
|
||||
|
||||
|
||||
@@ -28,9 +28,13 @@ class KomoditasController extends Controller
|
||||
public function store(Request $request)
|
||||
{
|
||||
$validator = Validator::make($request->all(), [
|
||||
'nama' => ['required', 'string', 'max:255', 'unique:master_komoditas,nama'], // Tambah unique biar nggak ada data kembar
|
||||
'nama' => ['required', 'string', 'max:255', 'unique:master_komoditas,nama'],
|
||||
'kode' => ['required', 'string', 'max:2', 'unique:master_komoditas,kode'],
|
||||
'is_utama' => ['nullable', 'boolean'],
|
||||
], [
|
||||
'nama.unique' => 'Nama komoditas ini sudah ada di database.'
|
||||
'nama.unique' => 'Nama komoditas ini sudah ada di database.',
|
||||
'kode.unique' => 'Kode komoditas ini sudah ada di database.',
|
||||
'kode.max' => 'Kode komoditas maksimal 2 Digit.'
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
@@ -39,6 +43,8 @@ class KomoditasController extends Controller
|
||||
|
||||
Komoditas::create([
|
||||
'nama' => $request->nama,
|
||||
'kode' => $request->kode,
|
||||
'is_utama' => $request->is_utama,
|
||||
'created_by' => auth()->id(),
|
||||
'updated_by' => auth()->id(),
|
||||
]);
|
||||
@@ -55,9 +61,13 @@ class KomoditasController extends Controller
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$validator = Validator::make($request->all(), [
|
||||
'nama' => ['required', 'string', 'max:255', 'unique:master_komoditas,nama,' . $id],
|
||||
'nama' => ['required', 'string', 'max:255', 'unique:master_komoditas,nama,' . $id],
|
||||
'kode' => ['required', 'string', 'max:2', 'unique:master_komoditas,kode,' . $id],
|
||||
'is_utama' => ['nullable', 'boolean'],
|
||||
], [
|
||||
'nama.unique' => 'Nama komoditas ini sudah digunakan.'
|
||||
'nama.unique' => 'Nama komoditas ini sudah ada di database.',
|
||||
'kode.unique' => 'Kode komoditas ini sudah ada di database.',
|
||||
'kode.max' => 'Kode komoditas maksimal 2 Digit.'
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
@@ -66,8 +76,10 @@ class KomoditasController extends Controller
|
||||
|
||||
$komoditas = Komoditas::findOrFail($id);
|
||||
$komoditas->update([
|
||||
'nama' => $request->nama,
|
||||
'updated_by' => auth()->id(),
|
||||
'nama' => $request->nama,
|
||||
'kode' => $request->kode,
|
||||
'is_utama' => $request->is_utama,
|
||||
'updated_by' => auth()->id(),
|
||||
]);
|
||||
|
||||
return response()->json(['status' => 'success', 'message' => 'Komoditas berhasil diupdate.']);
|
||||
|
||||
@@ -16,6 +16,7 @@ use App\Enums\AksesPasar\SkorPengalaman;
|
||||
use App\Enums\AksesPasar\SaluranTerdahulu;
|
||||
use App\Enums\AksesPasar\PraktikFermentasi;
|
||||
use App\Enums\AksesPasar\SkorKesadaranPremium;
|
||||
use App\Enums\AksesPasar\TingkatKepercayaanPembeli;
|
||||
|
||||
class AksesPasarController extends Controller
|
||||
{
|
||||
@@ -25,9 +26,25 @@ class AksesPasarController extends Controller
|
||||
|
||||
$aksesPasar = AksesPasar::where('petani_id', $petaniId)->first();
|
||||
$hargaJualPanen = HargaJualPanen::where('petani_id', $petaniId)->get();
|
||||
$hargaJualRows = collect([
|
||||
'Buah segar hasil petik',
|
||||
'Biji basah',
|
||||
'Biji kering/Biji Kupas',
|
||||
'Terfermentasi (Rp/kg)',
|
||||
])->map(function (string $bentuk, int $index) use ($hargaJualPanen) {
|
||||
$existingData = $hargaJualPanen->firstWhere('bentuk_produk', $bentuk);
|
||||
|
||||
return [
|
||||
'index' => $index,
|
||||
'bentuk' => $bentuk,
|
||||
'harga_panen_terakhir' => $this->currencyForDisplay($existingData?->harga_panen_terakhir),
|
||||
'harga_tahun_lalu' => $this->currencyForDisplay($existingData?->harga_tahun_lalu),
|
||||
'harga_dua_tahun_lalu' => $this->currencyForDisplay($existingData?->harga_dua_tahun_lalu),
|
||||
];
|
||||
});
|
||||
|
||||
return view('produktivitas.petani.akses-pasar.index', array_merge(
|
||||
compact('petani', 'aksesPasar', 'hargaJualPanen'),
|
||||
compact('petani', 'aksesPasar', 'hargaJualPanen', 'hargaJualRows'),
|
||||
$this->aksesPasarOptions()
|
||||
));
|
||||
}
|
||||
@@ -97,10 +114,12 @@ class AksesPasarController extends Controller
|
||||
'praktikFermentasiOptions' => PraktikFermentasi::options(),
|
||||
'praktikBelumPernahValue' => PraktikFermentasi::BELUM_PERNAH->value,
|
||||
'skorKesadaranPremiumOptions' => SkorKesadaranPremium::options(),
|
||||
'tingkatKepercayaanOptions' => TingkatKepercayaanPembeli::options(),
|
||||
'skorPengalamanBerhentiValues' => [
|
||||
SkorPengalaman::PERNAH_BERHENTI_TIDAK_ADA_AKSES->value,
|
||||
SkorPengalaman::PERNAH_BERHENTI_MUNGKIN_KEMBALI->value
|
||||
]
|
||||
],
|
||||
'sumberInfoLainnyaValue' => 'Lainnya',
|
||||
];
|
||||
}
|
||||
|
||||
@@ -110,6 +129,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'), [
|
||||
@@ -126,6 +156,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),
|
||||
@@ -139,6 +177,9 @@ class AksesPasarController extends Controller
|
||||
'skor_kesadaran_premium' => ['nullable', 'integer', Rule::in(SkorKesadaranPremium::values())],
|
||||
'sumber_info_premium' => ['nullable', 'string', 'max:255'],
|
||||
|
||||
'tingkat_kepercayaan_pembeli_baru' => ['nullable', 'integer', Rule::in(TingkatKepercayaanPembeli::values())],
|
||||
'alasan_kepercayaan_pembeli_baru' => ['nullable', 'string'],
|
||||
|
||||
'harga_jual' => ['nullable', 'array'],
|
||||
'harga_jual.*.bentuk_produk' => ['nullable', 'string'],
|
||||
'harga_jual.*.harga_panen_terakhir' => ['nullable', 'numeric', 'min:0'],
|
||||
@@ -147,6 +188,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.',
|
||||
@@ -183,6 +225,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
|
||||
@@ -194,6 +240,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;
|
||||
@@ -202,7 +252,6 @@ class AksesPasarController extends Controller
|
||||
return $payload;
|
||||
}
|
||||
|
||||
|
||||
private function syncHargaJual($petaniId, array $hargaJualData, $createdBy, $updatedBy = null): void
|
||||
{
|
||||
HargaJualPanen::where('petani_id', $petaniId)->delete();
|
||||
@@ -251,4 +300,11 @@ class AksesPasarController extends Controller
|
||||
$digits = preg_replace('/\D/', '', (string) $value);
|
||||
return $digits === '' ? null : $digits;
|
||||
}
|
||||
|
||||
private function currencyForDisplay(mixed $value): string
|
||||
{
|
||||
$digits = $this->currencyDigits($value);
|
||||
|
||||
return is_null($digits) ? '' : number_format((int) $digits, 0, ',', '.');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Web\Produktivitas;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
use App\Models\Pertanian\KelompokTani;
|
||||
use App\Models\Pertanian\AsetKelompokTani;
|
||||
|
||||
class AsetKelompokTaniController extends Controller
|
||||
{
|
||||
public const JENIS_ASET_BAKU = [
|
||||
'Bibit/alat produksi',
|
||||
'Pupuk (stok kelompok)',
|
||||
'Pulper/alat kupas',
|
||||
'Lantai jemur',
|
||||
'Kotak/wadah fermentasi',
|
||||
'Kas/simpanan kelompok',
|
||||
'Gudang/tempat simpan',
|
||||
'Timbangan',
|
||||
];
|
||||
|
||||
private function authorizeAccess(): void
|
||||
{
|
||||
if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) {
|
||||
abort(403);
|
||||
}
|
||||
}
|
||||
|
||||
private function redirectToSection(string $url, string $section, string $message)
|
||||
{
|
||||
return redirect($url . '#section-' . $section)->with([
|
||||
'success' => $message,
|
||||
'success_section' => $section,
|
||||
]);
|
||||
}
|
||||
|
||||
public function index(KelompokTani $kelompokTani)
|
||||
{
|
||||
$this->authorizeAccess();
|
||||
|
||||
$kelompokTani->load('aset');
|
||||
|
||||
return view('produktivitas.survey-kelompok-tani.aset-keuangan', compact('kelompokTani'));
|
||||
}
|
||||
|
||||
public function update(Request $request, KelompokTani $kelompokTani)
|
||||
{
|
||||
$this->authorizeAccess();
|
||||
|
||||
$validated = $request->validate([
|
||||
'aset' => ['required', 'array'],
|
||||
'aset.*.jenis_aset' => ['required', Rule::in(self::JENIS_ASET_BAKU)],
|
||||
'aset.*.status_aset' => ['nullable', 'boolean'],
|
||||
'aset.*.status_pengelolaan_aset' => ['nullable', 'string', 'max:100'],
|
||||
'aset.*.kondisi_aset' => ['nullable', 'string', 'max:100'],
|
||||
]);
|
||||
|
||||
foreach ($validated['aset'] as $item) {
|
||||
$jenisAset = $item['jenis_aset'];
|
||||
$ada = (bool) ($item['status_aset'] ?? false);
|
||||
|
||||
$existing = $kelompokTani->aset()->where('jenis_aset', $jenisAset)->first()
|
||||
?? $kelompokTani->aset()->onlyTrashed()->where('jenis_aset', $jenisAset)->latest('id')->first();
|
||||
|
||||
if (!$ada) {
|
||||
if ($existing && !$existing->trashed()) {
|
||||
$existing->update(['deleted_by' => auth()->id()]);
|
||||
$existing->delete();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($existing) {
|
||||
if ($existing->trashed()) {
|
||||
$existing->restore();
|
||||
}
|
||||
|
||||
$existing->update([
|
||||
'status_aset' => true,
|
||||
'status_pengelolaan_aset' => $item['status_pengelolaan_aset'] ?? $existing->status_pengelolaan_aset,
|
||||
'kondisi_aset' => $item['kondisi_aset'] ?? $existing->kondisi_aset,
|
||||
'deleted_by' => null,
|
||||
'updated_by' => auth()->id(),
|
||||
]);
|
||||
continue;
|
||||
}
|
||||
|
||||
$kelompokTani->aset()->create([
|
||||
'uid' => 'AKT-' . strtoupper(Str::random(10)),
|
||||
'jenis_aset' => $jenisAset,
|
||||
'status_aset' => true,
|
||||
'status_pengelolaan_aset' => $item['status_pengelolaan_aset'] ?? '-',
|
||||
'kondisi_aset' => $item['kondisi_aset'] ?? '-',
|
||||
'created_by' => auth()->id(),
|
||||
'updated_by' => auth()->id(),
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->redirectToSection(route('kelompok-tani.aset-keuangan.index', $kelompokTani->id), 'aset-baku', 'Data aset produktif berhasil disimpan.');
|
||||
}
|
||||
|
||||
public function store(Request $request, KelompokTani $kelompokTani)
|
||||
{
|
||||
$this->authorizeAccess();
|
||||
|
||||
$validated = $request->validate([
|
||||
'aset_lainnya' => ['nullable', 'array'],
|
||||
'aset_lainnya.*.jenis_aset' => ['nullable', 'string', 'max:255', Rule::notIn(self::JENIS_ASET_BAKU)],
|
||||
'aset_lainnya.*.status_pengelolaan_aset' => ['nullable', 'string', 'max:100'],
|
||||
'aset_lainnya.*.kondisi_aset' => ['nullable', 'string', 'max:100'],
|
||||
], [
|
||||
'aset_lainnya.*.jenis_aset.not_in' => 'Jenis aset ini sudah ada di daftar baku di atas.',
|
||||
]);
|
||||
|
||||
foreach ($validated['aset_lainnya'] ?? [] as $item) {
|
||||
$jenisAset = trim($item['jenis_aset'] ?? '');
|
||||
|
||||
if ($jenisAset === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$kelompokTani->aset()->create([
|
||||
'uid' => 'AKT-' . strtoupper(Str::random(10)),
|
||||
'jenis_aset' => $jenisAset,
|
||||
'status_aset' => true,
|
||||
'status_pengelolaan_aset' => $item['status_pengelolaan_aset'] ?? '-',
|
||||
'kondisi_aset' => $item['kondisi_aset'] ?? '-',
|
||||
'created_by' => auth()->id(),
|
||||
'updated_by' => auth()->id(),
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->redirectToSection(route('kelompok-tani.aset-keuangan.index', $kelompokTani->id), 'aset-lainnya', 'Aset lainnya berhasil ditambahkan.');
|
||||
}
|
||||
|
||||
public function destroy(KelompokTani $kelompokTani, AsetKelompokTani $aset)
|
||||
{
|
||||
$this->authorizeAccess();
|
||||
|
||||
abort_unless($aset->survey_kelompok_tani_id === $kelompokTani->id, 404);
|
||||
abort_if(in_array($aset->jenis_aset, self::JENIS_ASET_BAKU), 422, 'Aset baku diatur lewat checklist, bukan dihapus langsung.');
|
||||
|
||||
$aset->update(['deleted_by' => auth()->id()]);
|
||||
$aset->delete();
|
||||
|
||||
return $this->redirectToSection(route('kelompok-tani.aset-keuangan.index', $kelompokTani->id), 'aset-lainnya', 'Aset lainnya berhasil dihapus.');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Web\Produktivitas;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
use App\Models\Pertanian\KelompokTani;
|
||||
use App\Models\Pertanian\BantuanKelompokTani;
|
||||
|
||||
class BantuanKelompokTaniController extends Controller
|
||||
{
|
||||
private function authorizeAccess(): void
|
||||
{
|
||||
if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) {
|
||||
abort(403);
|
||||
}
|
||||
}
|
||||
|
||||
private function redirectToSection(string $url, string $section, string $message)
|
||||
{
|
||||
return redirect($url . '#section-' . $section)->with([
|
||||
'success' => $message,
|
||||
'success_section' => $section,
|
||||
]);
|
||||
}
|
||||
|
||||
private function redirectToSectionWithError(string $url, string $section, string $message)
|
||||
{
|
||||
return redirect($url . '#section-' . $section)->with([
|
||||
'error' => $message,
|
||||
'error_section' => $section,
|
||||
]);
|
||||
}
|
||||
|
||||
public function index(KelompokTani $kelompokTani)
|
||||
{
|
||||
$this->authorizeAccess();
|
||||
|
||||
$kelompokTani->load('bantuan');
|
||||
|
||||
return view('produktivitas.survey-kelompok-tani.bantuan-penilaian', compact('kelompokTani'));
|
||||
}
|
||||
|
||||
public function store(Request $request, KelompokTani $kelompokTani)
|
||||
{
|
||||
$this->authorizeAccess();
|
||||
|
||||
if (!$kelompokTani->pernah_mendapatkan_bantuan) {
|
||||
return $this->redirectToSectionWithError(
|
||||
route('kelompok-tani.bantuan-penilaian.index', $kelompokTani->id),
|
||||
'status-bantuan',
|
||||
'Set status jadi "Pernah" dulu sebelum menambahkan daftar bantuan/program.'
|
||||
);
|
||||
}
|
||||
|
||||
$validated = $request->validate([
|
||||
'bantuan' => ['nullable', 'array'],
|
||||
'bantuan.*.nama_program' => ['nullable', 'string', 'max:255'],
|
||||
'bantuan.*.penyelenggara' => ['nullable', 'string', 'max:255'],
|
||||
'bantuan.*.bentuk_bantuan' => ['nullable', 'string', 'max:255'],
|
||||
'bantuan.*.tahun' => ['nullable', 'integer', 'min:1900', 'max:' . date('Y')],
|
||||
]);
|
||||
|
||||
foreach ($validated['bantuan'] ?? [] as $item) {
|
||||
if (empty($item['nama_program']) && empty($item['penyelenggara']) && empty($item['bentuk_bantuan'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$kelompokTani->bantuan()->create([
|
||||
'uid' => 'BKT-' . strtoupper(Str::random(10)),
|
||||
'nama_program' => $item['nama_program'] ?? null,
|
||||
'penyelenggara' => $item['penyelenggara'] ?? null,
|
||||
'bentuk_bantuan' => $item['bentuk_bantuan'] ?? null,
|
||||
'tahun' => $item['tahun'] ?? null,
|
||||
'created_by' => auth()->id(),
|
||||
'updated_by' => auth()->id(),
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->redirectToSection(route('kelompok-tani.bantuan-penilaian.index', $kelompokTani->id), 'bantuan', 'Data bantuan berhasil ditambahkan.');
|
||||
}
|
||||
|
||||
public function destroy(KelompokTani $kelompokTani, BantuanKelompokTani $bantuan)
|
||||
{
|
||||
$this->authorizeAccess();
|
||||
|
||||
abort_unless($bantuan->survey_kelompok_tani_id === $kelompokTani->id, 404);
|
||||
|
||||
$bantuan->update(['deleted_by' => auth()->id()]);
|
||||
$bantuan->delete();
|
||||
|
||||
return $this->redirectToSection(route('kelompok-tani.bantuan-penilaian.index', $kelompokTani->id), 'bantuan', 'Data bantuan berhasil dihapus.');
|
||||
}
|
||||
}
|
||||
@@ -77,10 +77,11 @@ class PendidikanPelatihanController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
return back()->with('success', 'Riwayat pelatihan berhasil disimpan.');
|
||||
return $this->redirectBackToFragment($request, 'section-sertifikasi')
|
||||
->with('success', 'Riwayat pelatihan berhasil disimpan.');
|
||||
}
|
||||
|
||||
public function destroyPelatihan(Petani $petani, SurveyPelatihan $surveyPelatihan)
|
||||
public function destroyPelatihan(Request $request, Petani $petani, SurveyPelatihan $surveyPelatihan)
|
||||
{
|
||||
$this->authorizeAccess();
|
||||
|
||||
@@ -92,7 +93,8 @@ class PendidikanPelatihanController extends Controller
|
||||
|
||||
$surveyPelatihan->delete();
|
||||
|
||||
return back()->with('success', 'Riwayat pelatihan berhasil dihapus.');
|
||||
return $this->redirectBackToFragment($request, 'section-pelatihan')
|
||||
->with('success', 'Riwayat pelatihan berhasil dihapus.');
|
||||
}
|
||||
|
||||
public function storeSertifikasi(Request $request, Petani $petani)
|
||||
@@ -155,10 +157,11 @@ class PendidikanPelatihanController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
return back()->with('success', 'Sertifikasi berhasil disimpan.');
|
||||
return $this->redirectBackToFragment($request, 'section-sertifikasi')
|
||||
->with('success', 'Sertifikasi berhasil disimpan.');
|
||||
}
|
||||
|
||||
public function destroySertifikasi(Petani $petani, SurveySertifikasi $surveySertifikasi)
|
||||
public function destroySertifikasi(Request $request, Petani $petani, SurveySertifikasi $surveySertifikasi)
|
||||
{
|
||||
$this->authorizeAccess();
|
||||
|
||||
@@ -170,6 +173,18 @@ class PendidikanPelatihanController extends Controller
|
||||
|
||||
$surveySertifikasi->delete();
|
||||
|
||||
return back()->with('success', 'Sertifikasi berhasil dihapus.');
|
||||
return $this->redirectBackToFragment($request, 'section-sertifikasi')
|
||||
->with('success', 'Sertifikasi berhasil dihapus.');
|
||||
}
|
||||
|
||||
private function redirectBackToFragment(Request $request, string $defaultFragment)
|
||||
{
|
||||
$fragment = $request->input('redirect_fragment', $defaultFragment);
|
||||
|
||||
if (!is_string($fragment) || !preg_match('/^[A-Za-z0-9_-]+$/', $fragment)) {
|
||||
$fragment = $defaultFragment;
|
||||
}
|
||||
|
||||
return redirect()->to(Str::before(url()->previous(), '#') . '#' . $fragment);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Web\Produktivitas;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
use App\Models\Pertanian\KelompokTani;
|
||||
use App\Models\Pertanian\PengurusKelompokTani;
|
||||
|
||||
class PengurusKelompokTaniController extends Controller
|
||||
{
|
||||
private function authorizeAccess(): void
|
||||
{
|
||||
if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) {
|
||||
abort(403);
|
||||
}
|
||||
}
|
||||
|
||||
private function redirectToSection(string $url, string $section, string $message)
|
||||
{
|
||||
return redirect($url . '#section-' . $section)->with([
|
||||
'success' => $message,
|
||||
'success_section' => $section,
|
||||
]);
|
||||
}
|
||||
|
||||
public function index(KelompokTani $kelompokTani)
|
||||
{
|
||||
$this->authorizeAccess();
|
||||
|
||||
$kelompokTani->load('pengurus');
|
||||
|
||||
return view('produktivitas.survey-kelompok-tani.pengurus-anggota', compact('kelompokTani'));
|
||||
}
|
||||
|
||||
public function store(Request $request, KelompokTani $kelompokTani)
|
||||
{
|
||||
$this->authorizeAccess();
|
||||
|
||||
$validated = $request->validate([
|
||||
'pengurus' => ['nullable', 'array'],
|
||||
'pengurus.*.nama_pengurus' => ['nullable', 'string', 'max:255'],
|
||||
'pengurus.*.jabatan' => ['nullable', 'string', 'max:255'],
|
||||
'pengurus.*.kontak' => ['nullable', 'string', 'max:50'],
|
||||
'pengurus.*.status' => ['nullable', 'boolean'],
|
||||
]);
|
||||
|
||||
foreach ($validated['pengurus'] ?? [] as $item) {
|
||||
if (empty($item['nama_pengurus']) && empty($item['jabatan']) && empty($item['kontak'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$kelompokTani->pengurus()->create([
|
||||
'uid' => 'PKT-' . strtoupper(Str::random(10)),
|
||||
'nama_pengurus' => $item['nama_pengurus'] ?? null,
|
||||
'jabatan' => $item['jabatan'] ?? null,
|
||||
'kontak' => $item['kontak'] ?? null,
|
||||
'status' => $item['status'] ?? false,
|
||||
'created_by' => auth()->id(),
|
||||
'updated_by' => auth()->id(),
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->redirectToSection(route('kelompok-tani.pengurus-anggota.index', $kelompokTani->id), 'pengurus', 'Data pengurus berhasil ditambahkan.');
|
||||
}
|
||||
|
||||
public function destroy(KelompokTani $kelompokTani, PengurusKelompokTani $pengurus)
|
||||
{
|
||||
$this->authorizeAccess();
|
||||
|
||||
abort_unless($pengurus->survey_kelompok_tani_id === $kelompokTani->id, 404);
|
||||
|
||||
$pengurus->update(['deleted_by' => auth()->id()]);
|
||||
$pengurus->delete();
|
||||
|
||||
return $this->redirectToSection(route('kelompok-tani.pengurus-anggota.index', $kelompokTani->id), 'pengurus', 'Data pengurus berhasil dihapus.');
|
||||
}
|
||||
}
|
||||
@@ -12,9 +12,14 @@ use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
use App\Models\Wilayah\Provinsi;
|
||||
use App\Models\Wilayah\KabupatenKota;
|
||||
use App\Models\Wilayah\Kecamatan;
|
||||
use App\Models\Pertanian\Petani;
|
||||
use App\Models\Wilayah\DesaKelurahan;
|
||||
use App\Models\Pertanian\Komoditas;
|
||||
use App\Models\Pertanian\Tanaman;
|
||||
use App\Models\Pertanian\Lahan;
|
||||
use App\Models\Pertanian\SkorPetani;
|
||||
|
||||
use App\Enums\Pendidikan;
|
||||
use App\Enums\Pekerjaan;
|
||||
@@ -27,21 +32,186 @@ class PetaniController extends Controller
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$provinsi = Provinsi::orderBy('kode', 'asc')->get();
|
||||
if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$query = Petani::with('desakelurahan')
|
||||
->orderBy('created_at', 'desc');
|
||||
$provinsi = Provinsi::select('id', 'kode', 'nama')->orderBy('kode', 'asc')->get();
|
||||
$userRole = auth()->user()->role->value;
|
||||
$showFasilitatorColumn = in_array($userRole, ['super_admin', 'admin'], true);
|
||||
$defaultKabupaten = KabupatenKota::select('id', 'nama')
|
||||
->whereRaw('lower(nama) like ?', ['%ngada%'])
|
||||
->orderBy('nama')
|
||||
->first();
|
||||
$selectedKabupatenId = $request->query('kabupaten_kota_id', $defaultKabupaten?->id);
|
||||
$allowedSorts = ['nama_asc', 'nama_desc', 'luas_lahan_desc', 'luas_lahan_asc'];
|
||||
$requestedSort = $request->query('sort_by')
|
||||
?? match ($request->query('sort_nama')) {
|
||||
'asc' => 'nama_asc',
|
||||
'desc' => 'nama_desc',
|
||||
default => null,
|
||||
};
|
||||
|
||||
if ($request->search) {
|
||||
$query->where(function ($query) use ($request) {
|
||||
$query->where('nama', 'like', '%' . $request->search . '%')
|
||||
->orWhere('ktp', 'like', '%' . $request->search . '%');
|
||||
$filters = [
|
||||
'search' => $request->query('search'),
|
||||
'komoditas_id' => $request->query('komoditas_id'),
|
||||
'kabupaten_kota_id' => $selectedKabupatenId,
|
||||
'kecamatan_id' => $request->query('kecamatan_id'),
|
||||
'desa_kelurahan_id' => $request->query('desa_kelurahan_id'),
|
||||
'sort_by' => in_array($requestedSort, $allowedSorts, true)
|
||||
? $requestedSort
|
||||
: null,
|
||||
];
|
||||
|
||||
$query = Petani::with([
|
||||
'desaKelurahan:id,nama,kecamatan_id',
|
||||
'desaKelurahan.kecamatan:id,nama,kabupaten_kota_id',
|
||||
'desaKelurahan.kecamatan.kabupatenKota:id,nama,provinsi_id',
|
||||
'desaKelurahan.kecamatan.kabupatenKota.provinsi:id,nama',
|
||||
'komoditas:id,nama',
|
||||
'skor:id,petani_id,skor_kesiapan_adopsi',
|
||||
'creator:id,name,role',
|
||||
])
|
||||
->withSum('lahans as total_luas_lahan', 'luas_lahan');
|
||||
|
||||
$search = $request->filled('search') ? Str::lower($request->search) : null;
|
||||
if ($search) {
|
||||
$query->where(function ($query) use ($search) {
|
||||
$query->whereRaw('lower(nama) like ?', ["%{$search}%"])
|
||||
->orWhereRaw('lower(ktp) like ?', ["%{$search}%"]);
|
||||
});
|
||||
}
|
||||
|
||||
if ($request->filled('komoditas_id')) {
|
||||
$query->where('komoditas_id', $request->komoditas_id);
|
||||
}
|
||||
|
||||
if ($filters['kabupaten_kota_id']) {
|
||||
$query->whereHas('desaKelurahan.kecamatan', function ($query) use ($filters) {
|
||||
$query->where('kabupaten_kota_id', $filters['kabupaten_kota_id']);
|
||||
});
|
||||
}
|
||||
|
||||
if ($request->filled('kecamatan_id')) {
|
||||
$query->whereHas('desaKelurahan', function ($query) use ($request) {
|
||||
$query->where('kecamatan_id', $request->kecamatan_id);
|
||||
});
|
||||
}
|
||||
|
||||
if ($request->filled('desa_kelurahan_id')) {
|
||||
$query->where('desa_kelurahan_id', $request->desa_kelurahan_id);
|
||||
}
|
||||
|
||||
if ($userRole === 'fasilitator') {
|
||||
$query->where('created_by', auth()->user()->id);
|
||||
}
|
||||
|
||||
$totalPetani = (clone $query)->count();
|
||||
$totalPetaniLakiLaki = (clone $query)->where('jenis_kelamin', 1)->count();
|
||||
$totalPetaniPerempuan = (clone $query)->where('jenis_kelamin', 0)->count();
|
||||
|
||||
match ($filters['sort_by']) {
|
||||
'nama_asc' => $query->orderBy('nama', 'asc')->orderBy('created_at', 'desc'),
|
||||
'nama_desc' => $query->orderBy('nama', 'desc')->orderBy('created_at', 'desc'),
|
||||
'luas_lahan_desc' => $query->orderByDesc(
|
||||
Lahan::selectRaw('COALESCE(SUM(luas_lahan), 0)')
|
||||
->whereColumn('map_lahan.petani_id', 'petani.id')
|
||||
->whereNull('map_lahan.deleted_at')
|
||||
)->orderBy('created_at', 'desc'),
|
||||
'luas_lahan_asc' => $query->orderBy(
|
||||
Lahan::selectRaw('COALESCE(SUM(luas_lahan), 0)')
|
||||
->whereColumn('map_lahan.petani_id', 'petani.id')
|
||||
->whereNull('map_lahan.deleted_at')
|
||||
)->orderBy('created_at', 'desc'),
|
||||
default => $query->orderBy('created_at', 'desc'),
|
||||
};
|
||||
|
||||
$summaryPetaniIds = (clone $query)->pluck('id');
|
||||
$petani = $query->paginate($request->per_page ?? 10);
|
||||
|
||||
return view('produktivitas.petani.index', compact('petani', 'provinsi'));
|
||||
$komoditas = Komoditas::select('id', 'nama')->where('is_utama', true)->get();
|
||||
$komoditasOptions = Komoditas::select('id', 'nama')->orderBy('nama')->get();
|
||||
$kabupatenOptions = KabupatenKota::select('id', 'nama')->orderBy('nama')->get();
|
||||
$selectedKabupaten = $filters['kabupaten_kota_id']
|
||||
? KabupatenKota::select('id', 'nama')->find($filters['kabupaten_kota_id'])
|
||||
: null;
|
||||
$kecamatanOptions = $filters['kabupaten_kota_id']
|
||||
? Kecamatan::select('id', 'nama')->where('kabupaten_kota_id', $filters['kabupaten_kota_id'])->orderBy('nama')->get()
|
||||
: collect();
|
||||
$desaOptions = $request->filled('kecamatan_id')
|
||||
? DesaKelurahan::select('id', 'nama')->where('kecamatan_id', $request->kecamatan_id)->orderBy('nama')->get()
|
||||
: collect();
|
||||
$lahanSummaryQuery = Lahan::query()
|
||||
->whereHas('petani')
|
||||
->when($request->filled('komoditas_id'), function ($query) use ($request) {
|
||||
$query->where('komoditas_id', $request->komoditas_id);
|
||||
})
|
||||
->when($filters['kabupaten_kota_id'], function ($query) use ($filters) {
|
||||
$query->whereHas('desaKelurahan.kecamatan', function ($kecamatan) use ($filters) {
|
||||
$kecamatan->where('kabupaten_kota_id', $filters['kabupaten_kota_id']);
|
||||
});
|
||||
})
|
||||
->when($request->filled('kecamatan_id'), function ($query) use ($request) {
|
||||
$query->whereHas('desaKelurahan', function ($desa) use ($request) {
|
||||
$desa->where('kecamatan_id', $request->kecamatan_id);
|
||||
});
|
||||
})
|
||||
->when($request->filled('desa_kelurahan_id'), function ($query) use ($request) {
|
||||
$query->where('desa_kelurahan_id', $request->desa_kelurahan_id);
|
||||
})
|
||||
->when($request->filled('search'), function ($query) use ($search) {
|
||||
$query->whereHas('petani', function ($petani) use ($search) {
|
||||
$petani->whereRaw('lower(nama) like ?', ["%{$search}%"])
|
||||
->orWhereRaw('lower(ktp) like ?', ["%{$search}%"]);
|
||||
});
|
||||
})
|
||||
->when($userRole === 'fasilitator', function ($query) {
|
||||
$query->whereHas('petani', function ($petani) {
|
||||
$petani->where('created_by', auth()->id());
|
||||
});
|
||||
});
|
||||
|
||||
$totalLahan = (clone $lahanSummaryQuery)->count();
|
||||
$totalLuasLahan = (clone $lahanSummaryQuery)->sum('luas_lahan');
|
||||
$totalTanaman = Tanaman::whereIn('lahan_id', (clone $lahanSummaryQuery)->select('id'))
|
||||
->when($request->filled('komoditas_id'), function ($query) use ($request) {
|
||||
$query->where('komoditas_id', $request->komoditas_id);
|
||||
})
|
||||
->count();
|
||||
|
||||
$case = "
|
||||
CASE
|
||||
WHEN skor_kesiapan_adopsi >= 4.0 THEN 'Penggerak'
|
||||
WHEN skor_kesiapan_adopsi >= 3.0 THEN 'Perintis'
|
||||
WHEN skor_kesiapan_adopsi >= 2.0 THEN 'Pemerhati'
|
||||
WHEN skor_kesiapan_adopsi >= 1.0 THEN 'Pemula'
|
||||
ELSE 'Belum Lengkap'
|
||||
END
|
||||
";
|
||||
$skor = SkorPetani::selectRaw("$case AS segmen, COUNT(*) AS total")
|
||||
->whereIn('petani_id', $summaryPetaniIds)
|
||||
->groupByRaw($case)
|
||||
->get();
|
||||
|
||||
return view('produktivitas.petani.index', compact(
|
||||
'petani',
|
||||
'provinsi',
|
||||
'komoditas',
|
||||
'komoditasOptions',
|
||||
'kabupatenOptions',
|
||||
'selectedKabupaten',
|
||||
'kecamatanOptions',
|
||||
'desaOptions',
|
||||
'filters',
|
||||
'skor',
|
||||
'totalLahan',
|
||||
'totalLuasLahan',
|
||||
'totalTanaman',
|
||||
'totalPetani',
|
||||
'totalPetaniLakiLaki',
|
||||
'totalPetaniPerempuan',
|
||||
'showFasilitatorColumn'
|
||||
));
|
||||
}
|
||||
|
||||
public function create()
|
||||
@@ -54,7 +224,7 @@ class PetaniController extends Controller
|
||||
$pekerjaan = Pekerjaan::options();
|
||||
$pernikahan = Pernikahan::options();
|
||||
|
||||
$komoditas = Komoditas::all();
|
||||
$komoditas = Komoditas::where('is_utama', true)->get();
|
||||
|
||||
$provinsi = Provinsi::orderBy('kode', 'asc')->get();
|
||||
|
||||
@@ -145,7 +315,7 @@ class PetaniController extends Controller
|
||||
'created_by' => auth()->id(),
|
||||
'updated_by' => auth()->id()]);
|
||||
|
||||
return redirect()->route('petani.index')->with('success', 'Data petani berhasil ditambahkan.');
|
||||
return redirect()->route('petani.edit', $petani->id)->with('success', 'Data petani berhasil ditambahkan.');
|
||||
}
|
||||
|
||||
public function show(string $id)
|
||||
@@ -174,7 +344,7 @@ class PetaniController extends Controller
|
||||
|
||||
$provinsi = Provinsi::all();
|
||||
|
||||
$komoditas = Komoditas::all();
|
||||
$komoditas = Komoditas::where('is_utama', true)->get();
|
||||
|
||||
return view('produktivitas.petani.edit', compact('petani', 'pendidikan', 'pekerjaan',
|
||||
'pernikahan', 'provinsi', 'komoditas'));
|
||||
@@ -191,6 +361,13 @@ class PetaniController extends Controller
|
||||
|
||||
$validatedData = $this->validatePetani($request);
|
||||
|
||||
if ($request->input('hapus_foto') == '1') {
|
||||
if ($petani->foto && Storage::exists($petani->foto)) {
|
||||
Storage::delete($petani->foto);
|
||||
}
|
||||
$validatedData['foto'] = null;
|
||||
}
|
||||
|
||||
if ($request->hasFile('file_ktp')) {
|
||||
$file_ktp = $request->file('file_ktp');
|
||||
$validatedData['path_ktp'] = $file_ktp->store('petani/ktp');
|
||||
@@ -202,6 +379,10 @@ class PetaniController extends Controller
|
||||
$validatedData['file_kk'] = $file_kk->getClientOriginalName();
|
||||
}
|
||||
if ($request->hasFile('foto')) {
|
||||
|
||||
if ($petani->foto && Storage::exists($petani->foto)) {
|
||||
Storage::delete($petani->foto);
|
||||
}
|
||||
$file_foto = $request->file('foto');
|
||||
$validatedData['foto'] = $file_foto->store('petani/foto');
|
||||
}
|
||||
@@ -209,15 +390,41 @@ class PetaniController extends Controller
|
||||
$petani->update([...$validatedData,
|
||||
'updated_by' => auth()->id()]);
|
||||
|
||||
return redirect()->route('petani.index')->with('success', 'Data petani berhasil diupdate.');
|
||||
return redirect()->route('petani.edit', $petani->id)->with('success', 'Data petani berhasil diupdate.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy(string $id)
|
||||
{
|
||||
//
|
||||
if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) {
|
||||
return response()->json(['message' => 'Anda tidak memiliki akses.'], 403);
|
||||
}
|
||||
|
||||
try {
|
||||
$petani = Petani::findOrFail($id);
|
||||
|
||||
if (auth()->user()->role->value === 'fasilitator' && $petani->created_by !== auth()->id()) {
|
||||
return response()->json(['message' => 'Anda tidak memiliki akses.'], 403);
|
||||
}
|
||||
|
||||
DB::transaction(function () use ($petani) {
|
||||
$petani->update([
|
||||
'deleted_by' => auth()->id(),
|
||||
]);
|
||||
|
||||
$petani->delete();
|
||||
});
|
||||
|
||||
return response()->json([
|
||||
'status' => 'success',
|
||||
'message' => 'Data petani berhasil dihapus.'
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return response()->json([
|
||||
'status' => 'error',
|
||||
'message' => 'Gagal menghapus data: ' . $e->getMessage()
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
public function showImage(string $id, string $field)
|
||||
|
||||
@@ -7,6 +7,7 @@ use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Validation\Rules\Enum;
|
||||
@@ -17,7 +18,9 @@ use App\Models\Pertanian\Lahan;
|
||||
use App\Models\Wilayah\DesaKelurahan;
|
||||
use App\Models\Wilayah\Provinsi;
|
||||
use App\Models\Pertanian\Komoditas;
|
||||
use App\Models\Pertanian\PraktikBudidaya;
|
||||
use App\Models\Pertanian\Tanaman;
|
||||
use App\Models\Master\JenisTanaman;
|
||||
|
||||
use App\Enums\StatusLahan;
|
||||
use App\Enums\KondisiTanaman;
|
||||
@@ -62,8 +65,47 @@ class PetaniLahanController extends Controller
|
||||
$provinsi = Provinsi::orderBy('kode', 'asc')->get();
|
||||
|
||||
$komoditas = Komoditas::all();
|
||||
$tanamanPenaung = JenisTanaman::all();
|
||||
$autoPestDiseaseData = $this->autoPestDiseaseData($petaniId);
|
||||
$jenisHamaOtomatis = $autoPestDiseaseData['jenis_hama'];
|
||||
$jenisPenyakitOtomatis = $autoPestDiseaseData['jenis_penyakit'];
|
||||
|
||||
return view('produktivitas.petani.lahan.add', compact('petani', 'lahan', 'provinsi', 'komoditas'));
|
||||
return view('produktivitas.petani.lahan.add', compact('petani', 'lahan', 'provinsi', 'komoditas', 'tanamanPenaung', 'jenisHamaOtomatis', 'jenisPenyakitOtomatis'));
|
||||
}
|
||||
|
||||
private function autoPestDiseaseData(string|int $petaniId): array
|
||||
{
|
||||
$praktikBudidaya = PraktikBudidaya::where('petani_id', $petaniId)->latest()->first();
|
||||
|
||||
return [
|
||||
'jenis_hama' => $this->normalizeArrayValue($praktikBudidaya?->jenis_hama),
|
||||
'jenis_penyakit' => $this->normalizeArrayValue($praktikBudidaya?->jenis_penyakit),
|
||||
];
|
||||
}
|
||||
|
||||
private function normalizeArrayValue(mixed $value): array
|
||||
{
|
||||
if (is_array($value)) {
|
||||
return array_values(array_filter($value, fn ($item) => filled($item)));
|
||||
}
|
||||
|
||||
if (is_string($value) && $value !== '') {
|
||||
$decoded = json_decode($value, true);
|
||||
|
||||
if (is_array($decoded)) {
|
||||
return array_values(array_filter($decoded, fn ($item) => filled($item)));
|
||||
}
|
||||
|
||||
return [$value];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
private function hasPestDiseaseColumns(string $table): bool
|
||||
{
|
||||
return Schema::hasColumn($table, 'jenis_hama')
|
||||
&& Schema::hasColumn($table, 'jenis_penyakit');
|
||||
}
|
||||
|
||||
private function validateLahan(Request $request)
|
||||
@@ -81,8 +123,8 @@ class PetaniLahanController extends Controller
|
||||
'komoditas_id' => ['required', 'exists:master_komoditas,id'],
|
||||
'jenis_komoditas_id' => ['required', 'exists:jenis_komoditas,id'],
|
||||
'varietas_id' => ['nullable', 'exists:master_varietas,id'],
|
||||
'jumlah_tanaman' => ['required', 'numeric', 'min:1'],
|
||||
'umur_lahan' => ['required', 'numeric', 'min:1'],
|
||||
'jumlah_tanaman' => ['nullable', 'numeric', 'min:0'],
|
||||
'umur_lahan' => ['nullable', 'numeric', 'min:1'],
|
||||
'umur_tanaman' => ['required', 'numeric', 'min:1'],
|
||||
'kondisi_tanaman' => ['required', 'string', new Enum(KondisiTanaman::class)],
|
||||
'serangan_hama' => ['nullable', 'string', new Enum(SeranganHama::class)],
|
||||
@@ -91,7 +133,7 @@ class PetaniLahanController extends Controller
|
||||
'komoditas_lain' => ['nullable', 'array'],
|
||||
'komoditas_lain.*' => ['integer', 'exists:master_komoditas,id'],
|
||||
'tanaman_penaung' => ['nullable', 'array'],
|
||||
'tanaman_penaung.*' => ['integer', 'exists:master_komoditas,id'],
|
||||
'tanaman_penaung.*' => ['string'],
|
||||
'foto' => ['nullable', 'array'],
|
||||
'foto.*' => ['image', 'mimes:jpeg,jpg,png', 'max:2048'],
|
||||
], [
|
||||
@@ -102,10 +144,10 @@ class PetaniLahanController extends Controller
|
||||
'luas_lahan.required' => 'Luas lahan wajib diisi.',
|
||||
'luas_lahan.numeric' => 'Luas lahan diisi dengan angka.',
|
||||
'luas_lahan.min' => 'Luas lahan minimal 0.01.',
|
||||
'jumlah_tanaman.required' => 'Jumlah tanaman wajib diisi.',
|
||||
//'jumlah_tanaman.required' => 'Jumlah tanaman wajib diisi.',
|
||||
'jumlah_tanaman.numeric' => 'Jumlah tanaman diisi dengan angka.',
|
||||
'jumlah_tanaman.min' => 'Jumlah tanaman minimal 1.',
|
||||
'umur_lahan.required' => 'Umur lahan wajib diisi.',
|
||||
'jumlah_tanaman.min' => 'Jumlah tanaman minimal 0.',
|
||||
//'umur_lahan.required' => 'Umur lahan wajib diisi.',
|
||||
'umur_lahan.numeric' => 'Umur lahan diisi dengan angka.',
|
||||
'umur_lahan.min' => 'Umur lahan minimal 1.',
|
||||
'umur_tanaman.required' => 'Umur tanaman wajib diisi.',
|
||||
@@ -119,7 +161,7 @@ class PetaniLahanController extends Controller
|
||||
'komoditas_lain.array' => 'Komoditas lain diisi dengan array.',
|
||||
'komoditas_lain.*.exists' => 'Komoditas lain yang dipilih tidak valid.',
|
||||
'tanaman_penaung.array' => 'Tanaman penaung diisi dengan array.',
|
||||
'tanaman_penaung.*.exists' => 'Tanaman penaung yang dipilih tidak valid.',
|
||||
//'tanaman_penaung.*.exists' => 'Tanaman penaung yang dipilih tidak valid.',
|
||||
'foto.array' => 'Foto diisi dengan array.',
|
||||
'foto.*.image' => 'Foto harus berupa gambar.',
|
||||
'foto.*.mimes' => 'Foto harus berupa gambar dengan ekstensi jpeg, jpg, atau png.',
|
||||
@@ -128,6 +170,28 @@ class PetaniLahanController extends Controller
|
||||
|
||||
}
|
||||
|
||||
private function syncTanamanPenaung(Request $request)
|
||||
{
|
||||
$existing = JenisTanaman::pluck('nama')->toArray();
|
||||
|
||||
$tanamanPenaung = collect($request->tanaman_penaung)
|
||||
->unique()
|
||||
->reject(fn ($nama) => in_array($nama, $existing))
|
||||
->map(fn ($nama) => [
|
||||
'nama' => Str::title($nama),
|
||||
'created_by' => auth()->id(),
|
||||
'updated_by' => auth()->id(),
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
])
|
||||
->values()
|
||||
->toArray();
|
||||
|
||||
if (!empty($tanamanPenaung)) {
|
||||
JenisTanaman::insert($tanamanPenaung);
|
||||
}
|
||||
}
|
||||
|
||||
public function store($petaniId, Request $request)
|
||||
{
|
||||
if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) {
|
||||
@@ -136,7 +200,21 @@ class PetaniLahanController extends Controller
|
||||
|
||||
$petani = Petani::findOrFail($petaniId);
|
||||
|
||||
$request->merge([
|
||||
'tanaman_penaung' => collect($request->tanaman_penaung)
|
||||
->map(fn ($nama) => Str::title($nama))
|
||||
->toArray()
|
||||
]);
|
||||
|
||||
$validated = $this->validateLahan($request);
|
||||
$autoPestDiseaseData = $this->autoPestDiseaseData($petaniId);
|
||||
$lahanHasPestDiseaseColumns = $this->hasPestDiseaseColumns((new Lahan)->getTable());
|
||||
$tanamanHasPestDiseaseColumns = $this->hasPestDiseaseColumns((new Tanaman)->getTable());
|
||||
|
||||
if ($lahanHasPestDiseaseColumns) {
|
||||
$validated = array_merge($validated, $autoPestDiseaseData);
|
||||
}
|
||||
|
||||
$komoditas = Komoditas::where('id', $request->komoditas_id)->first();
|
||||
$lahanCount = Lahan::where('petani_id', $petaniId)
|
||||
->count();
|
||||
@@ -154,6 +232,8 @@ class PetaniLahanController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
$this->syncTanamanPenaung($request);
|
||||
|
||||
$lahan = Lahan::create([...$validated,
|
||||
'foto' => $foto && count($foto) > 0 ? json_encode($foto) : null,
|
||||
'nama' => 'Lahan ' . $komoditas->nama . ' #' . ($lahanCount + 1),
|
||||
@@ -163,9 +243,9 @@ class PetaniLahanController extends Controller
|
||||
'updated_by' => auth()->id()]
|
||||
);
|
||||
|
||||
if ($lahan) {
|
||||
$data = collect(range(1, $lahan->jumlah_tanaman))->map(function ($i) use ($lahan, $komoditas, $lahanUid) {
|
||||
return [
|
||||
if ($lahan && $lahan->jumlah_tanaman > 0) {
|
||||
$data = collect(range(1, $lahan->jumlah_tanaman))->map(function ($i) use ($lahan, $komoditas, $lahanUid, $autoPestDiseaseData, $tanamanHasPestDiseaseColumns) {
|
||||
$row = [
|
||||
'lahan_id' => $lahan->id,
|
||||
'uid' => 'TNM-' . $lahanUid . '-' . strtoupper(Str::random(10)),
|
||||
'komoditas_id' => $lahan->komoditas_id,
|
||||
@@ -183,6 +263,13 @@ class PetaniLahanController extends Controller
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
];
|
||||
|
||||
if ($tanamanHasPestDiseaseColumns) {
|
||||
$row['jenis_hama'] = json_encode($autoPestDiseaseData['jenis_hama'] ?? []);
|
||||
$row['jenis_penyakit'] = json_encode($autoPestDiseaseData['jenis_penyakit'] ?? []);
|
||||
}
|
||||
|
||||
return $row;
|
||||
});
|
||||
|
||||
$data->chunk(500)->each(function ($chunk) {
|
||||
@@ -192,13 +279,15 @@ class PetaniLahanController extends Controller
|
||||
if ($request->ajax()) {
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'redirect' => route('petani.lahan-tanaman.index', $petaniId),
|
||||
'redirect' => route('petani.lahan-tanaman.edit', [$petaniId, $lahan->id]),
|
||||
'message' => 'Lahan berhasil ditambahkan.'
|
||||
]);
|
||||
}
|
||||
|
||||
return redirect()->route('petani.lahan-tanaman.index', $petaniId)->with('success', 'Lahan berhasil ditambahkan.');
|
||||
return redirect()->route('petani.lahan-tanaman.edit', [$petaniId, $lahan->id])->with('success', 'Lahan berhasil ditambahkan.');
|
||||
}
|
||||
|
||||
return redirect()->route('petani.lahan-tanaman.edit', [$petaniId, $lahan->id])->with('success', 'Lahan berhasil ditambahkan.');
|
||||
}
|
||||
|
||||
public function show(string $petaniId, string $id)
|
||||
@@ -234,10 +323,14 @@ class PetaniLahanController extends Controller
|
||||
$lahanSelected = Lahan::where('petani_id', $petaniId)->findOrFail($id);
|
||||
|
||||
$komoditas = Komoditas::all();
|
||||
$tanamanPenaung = JenisTanaman::all();
|
||||
$autoPestDiseaseData = $this->autoPestDiseaseData($petaniId);
|
||||
$jenisHamaOtomatis = $autoPestDiseaseData['jenis_hama'];
|
||||
$jenisPenyakitOtomatis = $autoPestDiseaseData['jenis_penyakit'];
|
||||
|
||||
$provinsi = Provinsi::orderBy('kode', 'asc')->get();
|
||||
|
||||
return view('produktivitas.petani.lahan.edit', compact('lahan', 'lahanSelected', 'petani', 'komoditas', 'provinsi'));
|
||||
return view('produktivitas.petani.lahan.edit', compact('lahan', 'lahanSelected', 'petani', 'komoditas', 'provinsi', 'tanamanPenaung', 'jenisHamaOtomatis', 'jenisPenyakitOtomatis'));
|
||||
}
|
||||
|
||||
public function update(Request $request, string $petaniId, string $id)
|
||||
@@ -249,14 +342,62 @@ class PetaniLahanController extends Controller
|
||||
$petani = Petani::findOrFail($petaniId);
|
||||
$lahan = Lahan::where('petani_id', $petaniId)->findOrFail($id);
|
||||
|
||||
$request->merge([
|
||||
'tanaman_penaung' => collect($request->tanaman_penaung)
|
||||
->map(fn ($nama) => Str::title($nama))
|
||||
->toArray()
|
||||
]);
|
||||
$validated = $this->validateLahan($request);
|
||||
$autoPestDiseaseData = $this->autoPestDiseaseData($petaniId);
|
||||
$lahanHasPestDiseaseColumns = $this->hasPestDiseaseColumns((new Lahan)->getTable());
|
||||
$tanamanHasPestDiseaseColumns = $this->hasPestDiseaseColumns((new Tanaman)->getTable());
|
||||
|
||||
if ($lahanHasPestDiseaseColumns) {
|
||||
$validated = array_merge($validated, $autoPestDiseaseData);
|
||||
}
|
||||
|
||||
$komoditas = Komoditas::where('id', $request->komoditas_id)->first();
|
||||
$tanamanCount = Tanaman::where('lahan_id', $lahan->id)->count();
|
||||
$lahanCount = Lahan::where('petani_id', $petaniId)->count();
|
||||
|
||||
$fotoLama = $lahan->foto ?? [];
|
||||
if (is_string($fotoLama)) {
|
||||
$fotoLama = json_decode($fotoLama, true);
|
||||
}
|
||||
if (!is_array($fotoLama)) {
|
||||
$fotoLama = [];
|
||||
}
|
||||
|
||||
if ($request->has('deleted_photos') && is_array($request->deleted_photos)) {
|
||||
foreach ($request->deleted_photos as $deletedPath) {
|
||||
|
||||
if (Storage::disk('public')->exists($deletedPath)) {
|
||||
Storage::disk('public')->delete($deletedPath);
|
||||
}
|
||||
|
||||
$fotoLama = array_filter($fotoLama, function($item) use ($deletedPath) {
|
||||
$currentPath = is_array($item) ? ($item['path'] ?? '') : $item;
|
||||
return $currentPath !== $deletedPath;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$fotoLama = array_values($fotoLama);
|
||||
|
||||
if ($request->hasFile('foto')) {
|
||||
foreach ($request->file('foto') as $file) {
|
||||
$path = $file->store('lahan', 'public');
|
||||
$fotoLama[] = [
|
||||
'path' => $path,
|
||||
'file' => $file->getClientOriginalName(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
unset($validated['foto']);
|
||||
|
||||
$lahan->update([...$validated,
|
||||
//'nama' => 'Lahan ' . $komoditas->nama . ' #' . ($lahanCount + 1),
|
||||
//'petani_id' => $petaniId,
|
||||
'foto' => count($fotoLama) > 0 ? json_encode($fotoLama) : null,
|
||||
'updated_by' => auth()->id()]
|
||||
);
|
||||
|
||||
@@ -264,9 +405,11 @@ class PetaniLahanController extends Controller
|
||||
$lahanUidParts = explode('LHN-', $lahanUid);
|
||||
$lahanUid = end($lahanUidParts);
|
||||
|
||||
$this->syncTanamanPenaung($request);
|
||||
|
||||
if ($lahan && $lahan->jumlah_tanaman > $tanamanCount) {
|
||||
$data = collect(range($tanamanCount + 1, $lahan->jumlah_tanaman))->map(function ($i) use ($lahan, $komoditas, $lahanUid) {
|
||||
return [
|
||||
$data = collect(range($tanamanCount + 1, $lahan->jumlah_tanaman))->map(function ($i) use ($lahan, $komoditas, $lahanUid, $autoPestDiseaseData, $tanamanHasPestDiseaseColumns) {
|
||||
$row = [
|
||||
'lahan_id' => $lahan->id,
|
||||
'komoditas_id' => $lahan->komoditas_id,
|
||||
'jenis_komoditas_id' => $lahan->jenis_komoditas_id,
|
||||
@@ -284,6 +427,13 @@ class PetaniLahanController extends Controller
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
];
|
||||
|
||||
if ($tanamanHasPestDiseaseColumns) {
|
||||
$row['jenis_hama'] = json_encode($autoPestDiseaseData['jenis_hama'] ?? []);
|
||||
$row['jenis_penyakit'] = json_encode($autoPestDiseaseData['jenis_penyakit'] ?? []);
|
||||
}
|
||||
|
||||
return $row;
|
||||
});
|
||||
|
||||
$data->chunk(500)->each(function ($chunk) {
|
||||
@@ -291,12 +441,42 @@ class PetaniLahanController extends Controller
|
||||
});
|
||||
}
|
||||
|
||||
return redirect()->route('petani.lahan-tanaman.index', $petaniId)->with('success', 'Lahan berhasil ditambahkan.');
|
||||
if ($tanamanHasPestDiseaseColumns) {
|
||||
$lahan->tanaman()->update([
|
||||
'jenis_hama' => json_encode($autoPestDiseaseData['jenis_hama'] ?? []),
|
||||
'jenis_penyakit' => json_encode($autoPestDiseaseData['jenis_penyakit'] ?? []),
|
||||
'updated_by' => auth()->id(),
|
||||
]);
|
||||
}
|
||||
|
||||
if ($request->ajax()) {
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'redirect' => route('petani.lahan-tanaman.edit', [$petaniId, $lahan->id]),
|
||||
'message' => 'Lahan berhasil diupdate.'
|
||||
]);
|
||||
}
|
||||
|
||||
return redirect()->route('petani.lahan-tanaman.edit', [$petaniId, $lahan->id])->with('success', 'Lahan berhasil diperbarui.');
|
||||
}
|
||||
|
||||
public function destroy(string $id)
|
||||
public function destroy(string $petaniId, string $id)
|
||||
{
|
||||
//
|
||||
if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$lahan = Lahan::findOrFail($id);
|
||||
$lahan->tanaman()->update([
|
||||
'deleted_by' => auth()->id()
|
||||
]);
|
||||
$lahan->tanaman()->delete();
|
||||
$lahan->update([
|
||||
'deleted_by' => auth()->id()
|
||||
]);
|
||||
$lahan->delete();
|
||||
|
||||
return redirect()->route('petani.lahan-tanaman.index', $petaniId)->with('success', 'Lahan berhasil dihapus.');
|
||||
}
|
||||
|
||||
public function summary(string $petaniId)
|
||||
|
||||
@@ -178,8 +178,14 @@ class PraktikBudidayaController extends Controller
|
||||
|
||||
$teknik_pemangkasan = array_values($teknik_pemangkasan);
|
||||
|
||||
if ((int) ($validatedData['frekuensi_pemangkasan_komoditas_utama'] ?? 0) === FrekuensiPemangkasanUtama::TIDAK_PERNAH->value) {
|
||||
$teknik_pemangkasan = [];
|
||||
}
|
||||
|
||||
$petani = Petani::findOrFail($petaniId);
|
||||
|
||||
$pemupukanTidakPernah = (int) ($validatedData['pemberian_pupuk'] ?? 0) === PemberianPupuk::TIDAK_PERNAH->value;
|
||||
|
||||
$praktikBudidaya = $petani->praktikBudidaya()->create([
|
||||
'petani_id' => $petani->id,
|
||||
'uid' => 'PB-' . strtoupper(Str::random(10)),
|
||||
@@ -193,12 +199,12 @@ class PraktikBudidayaController extends Controller
|
||||
'deskripsi_penyerbukan_manual' => $validatedData['alasan_tidak_penyerbukan'] ?? null,
|
||||
'tingkat_keberhasilan_penyerbukan' => $validatedData['tingkat_keberhasilan_penyerbukan'] ?? null,
|
||||
'pemberian_pupuk' => $validatedData['pemberian_pupuk'] ?? null,
|
||||
'jenis_pupuk' => $validatedData['jenis_pupuk'] ?? null,
|
||||
'frekuensi_pemberian_pupuk' => ($validatedData['frekuensi_pemupukan'] ?? null) === 'Lainnya' ? ($validatedData['frekuensi_pemupukan_lainnya'] ?? null) : ($validatedData['frekuensi_pemupukan'] ?? null),
|
||||
'estimasi_dosis_pupuk_organik' => $validatedData['estimasi_dosis_pupuk_organik'] ?? null,
|
||||
'estimasi_dosis_pupuk_anorganik' => $validatedData['estimasi_dosis_pupuk_anorganik'] ?? null,
|
||||
'waktu_pemberian_pupuk' => ($validatedData['waktu_pemberian_pupuk'] ?? null) === 'Lainnya' ? ($validatedData['waktu_pemberian_pupuk_lainnya'] ?? null) : ($validatedData['waktu_pemberian_pupuk'] ?? null),
|
||||
'cara_pemberian_pupuk' => ($validatedData['cara_pemberian_pupuk'] ?? null) === 'Lainnya' ? ($validatedData['cara_pemberian_pupuk_lainnya'] ?? null) : ($validatedData['cara_pemberian_pupuk'] ?? null),
|
||||
'jenis_pupuk' => $pemupukanTidakPernah ? null : ($validatedData['jenis_pupuk'] ?? null),
|
||||
'frekuensi_pemberian_pupuk' => $pemupukanTidakPernah ? null : (($validatedData['frekuensi_pemupukan'] ?? null) === 'Lainnya' ? ($validatedData['frekuensi_pemupukan_lainnya'] ?? null) : ($validatedData['frekuensi_pemupukan'] ?? null)),
|
||||
'estimasi_dosis_pupuk_organik' => $pemupukanTidakPernah ? null : ($validatedData['estimasi_dosis_pupuk_organik'] ?? null),
|
||||
'estimasi_dosis_pupuk_anorganik' => $pemupukanTidakPernah ? null : ($validatedData['estimasi_dosis_pupuk_anorganik'] ?? null),
|
||||
'waktu_pemberian_pupuk' => $pemupukanTidakPernah ? null : (($validatedData['waktu_pemberian_pupuk'] ?? null) === 'Lainnya' ? ($validatedData['waktu_pemberian_pupuk_lainnya'] ?? null) : ($validatedData['waktu_pemberian_pupuk'] ?? null)),
|
||||
'cara_pemberian_pupuk' => $pemupukanTidakPernah ? null : (($validatedData['cara_pemberian_pupuk'] ?? null) === 'Lainnya' ? ($validatedData['cara_pemberian_pupuk_lainnya'] ?? null) : ($validatedData['cara_pemberian_pupuk'] ?? null)),
|
||||
'created_by' => auth()->id(),
|
||||
'updated_by' => auth()->id(),
|
||||
], [
|
||||
@@ -555,6 +561,12 @@ class PraktikBudidayaController extends Controller
|
||||
}
|
||||
$teknikPemangkasan = array_values($teknik_pemangkasan);
|
||||
|
||||
if ((int) ($validatedData['frekuensi_pemangkasan_komoditas_utama'] ?? 0) === FrekuensiPemangkasanUtama::TIDAK_PERNAH->value) {
|
||||
$teknikPemangkasan = [];
|
||||
}
|
||||
|
||||
$pemupukanTidakPernah = (int) ($validatedData['pemberian_pupuk'] ?? 0) === PemberianPupuk::TIDAK_PERNAH->value;
|
||||
|
||||
$dataPraktikBudidaya = [
|
||||
'tenaga_kerja' => $validatedData['tenaga_kerja'],
|
||||
'frekuensi_pemangkasan_utama' => $validatedData['frekuensi_pemangkasan_komoditas_utama'],
|
||||
@@ -566,12 +578,12 @@ class PraktikBudidayaController extends Controller
|
||||
'deskripsi_penyerbukan_manual' => $validatedData['alasan_tidak_penyerbukan'] ?? null,
|
||||
'tingkat_keberhasilan_penyerbukan' => $validatedData['tingkat_keberhasilan_penyerbukan'] ?? null,
|
||||
'pemberian_pupuk' => $validatedData['pemberian_pupuk'] ?? null,
|
||||
'jenis_pupuk' => $validatedData['jenis_pupuk'] ?? null,
|
||||
'frekuensi_pemberian_pupuk' => ($validatedData['frekuensi_pemupukan'] ?? null) === 'Lainnya' ? ($validatedData['frekuensi_pemupukan_lainnya'] ?? null) : ($validatedData['frekuensi_pemupukan'] ?? null),
|
||||
'estimasi_dosis_pupuk_organik' => $validatedData['estimasi_dosis_pupuk_organik'] ?? null,
|
||||
'estimasi_dosis_pupuk_anorganik' => $validatedData['estimasi_dosis_pupuk_anorganik'] ?? null,
|
||||
'waktu_pemberian_pupuk' => ($validatedData['waktu_pemberian_pupuk'] ?? null) === 'Lainnya' ? ($validatedData['waktu_pemberian_pupuk_lainnya'] ?? null) : ($validatedData['waktu_pemberian_pupuk'] ?? null),
|
||||
'cara_pemberian_pupuk' => ($validatedData['cara_pemberian_pupuk'] ?? null) === 'Lainnya' ? ($validatedData['cara_pemberian_pupuk_lainnya'] ?? null) : ($validatedData['cara_pemberian_pupuk'] ?? null),
|
||||
'jenis_pupuk' => $pemupukanTidakPernah ? null : ($validatedData['jenis_pupuk'] ?? null),
|
||||
'frekuensi_pemberian_pupuk' => $pemupukanTidakPernah ? null : (($validatedData['frekuensi_pemupukan'] ?? null) === 'Lainnya' ? ($validatedData['frekuensi_pemupukan_lainnya'] ?? null) : ($validatedData['frekuensi_pemupukan'] ?? null)),
|
||||
'estimasi_dosis_pupuk_organik' => $pemupukanTidakPernah ? null : ($validatedData['estimasi_dosis_pupuk_organik'] ?? null),
|
||||
'estimasi_dosis_pupuk_anorganik' => $pemupukanTidakPernah ? null : ($validatedData['estimasi_dosis_pupuk_anorganik'] ?? null),
|
||||
'waktu_pemberian_pupuk' => $pemupukanTidakPernah ? null : (($validatedData['waktu_pemberian_pupuk'] ?? null) === 'Lainnya' ? ($validatedData['waktu_pemberian_pupuk_lainnya'] ?? null) : ($validatedData['waktu_pemberian_pupuk'] ?? null)),
|
||||
'cara_pemberian_pupuk' => $pemupukanTidakPernah ? null : (($validatedData['cara_pemberian_pupuk'] ?? null) === 'Lainnya' ? ($validatedData['cara_pemberian_pupuk_lainnya'] ?? null) : ($validatedData['cara_pemberian_pupuk'] ?? null)),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -675,6 +687,17 @@ class PraktikBudidayaController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
return back()->with('success', 'Data berhasil diperbarui.');
|
||||
return $this->redirectBackToFragment($request)->with('success', 'Data berhasil diperbarui.');
|
||||
}
|
||||
|
||||
private function redirectBackToFragment(Request $request)
|
||||
{
|
||||
$fragment = $request->input('redirect_fragment');
|
||||
|
||||
if (!is_string($fragment) || !preg_match('/^[A-Za-z0-9_-]+$/', $fragment)) {
|
||||
return back();
|
||||
}
|
||||
|
||||
return redirect()->to(Str::before(url()->previous(), '#') . '#' . $fragment);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,978 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Web\Produktivitas;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
use App\Models\Pertanian\Petani;
|
||||
use App\Models\Pertanian\SkorPetani;
|
||||
use App\Models\Pertanian\Komoditas;
|
||||
use App\Models\Pertanian\Lahan;
|
||||
use App\Models\Pertanian\Tanaman;
|
||||
use App\Models\Pertanian\PraktikBudidaya;
|
||||
use App\Models\Pertanian\SurveyPanen;
|
||||
use App\Models\Pertanian\SosialEkonomi;
|
||||
use App\Models\Pertanian\SurveyPelatihan;
|
||||
use App\Models\Pertanian\SurveySertifikasi;
|
||||
use App\Models\Pertanian\AksesPasar;
|
||||
use App\Models\Pertanian\HargaJualPanen;
|
||||
use App\Models\Pertanian\PendapatanKomoditas;
|
||||
use App\Models\Wilayah\DesaKelurahan;
|
||||
use App\Models\Profile;
|
||||
|
||||
use App\Enums\PraktikBudidaya\FrekuensiPemangkasanUtama;
|
||||
use App\Enums\PraktikBudidaya\PemberianPupuk;
|
||||
use App\Enums\PraktikBudidaya\MetodePenangananHamaPenyakit;
|
||||
use App\Enums\PraktikBudidaya\FrekuensiSanitasiLahan;
|
||||
use App\Enums\PraktikBudidaya\PenangananGulma;
|
||||
use App\Enums\PraktikBudidaya\KondisiPengelolaanAir;
|
||||
use App\Enums\SosialEkonomi\SkorKecukupanPenghasilan;
|
||||
use App\Enums\SosialEkonomi\SkorKemampuanMenabung;
|
||||
use App\Enums\SosialEkonomi\TerdaftarKelompokTani;
|
||||
use App\Enums\SosialEkonomi\PeranDalamKelompok;
|
||||
use App\Enums\SosialEkonomi\StatusRekeningTabungan;
|
||||
use App\Enums\SosialEkonomi\PengalamanKreditKebun;
|
||||
use App\Enums\SosialEkonomi\KeterikatanHutangPanen;
|
||||
use App\Enums\SosialEkonomi\PraktikIjon;
|
||||
use App\Enums\SosialEkonomi\KebebasanMemilihPembeli;
|
||||
|
||||
class ProfilPetaniController extends Controller
|
||||
{
|
||||
private const DIMENSION_LOW_MAX = 2.49;
|
||||
private const DIMENSION_MEDIUM_MAX = 3.49;
|
||||
|
||||
public function index(int $id)
|
||||
{
|
||||
$petani = Petani::findOrFail($id);
|
||||
$skorPetani = SkorPetani::where('petani_id', $id)->first();
|
||||
$enumerator = Profile::where('user_id', $petani->created_by)->first();
|
||||
$komoditasUtama = Komoditas::where('id', $petani->komoditas_id)->first();
|
||||
$lahan = Lahan::query()
|
||||
->select(['id', 'petani_id', 'nama', 'path', 'luas_lahan', 'komoditas_id', 'desa_kelurahan_id'])
|
||||
->with([
|
||||
'komoditas:id,nama',
|
||||
'desaKelurahan:id,nama,path,kecamatan_id',
|
||||
'desaKelurahan.kecamatan:id,nama,path',
|
||||
])
|
||||
->where('petani_id', $id)
|
||||
->get();
|
||||
|
||||
$desaKelurahan = DesaKelurahan::with('kecamatan')
|
||||
->where('id', $petani->desa_kelurahan_id)
|
||||
->first();
|
||||
|
||||
$ktpMasked = $petani->ktp ? Str::mask($petani->ktp, '*', 4, -4) : '-';
|
||||
$assessmentFields = [
|
||||
'skor_kesiapan_adopsi',
|
||||
'skor_d2_sistem_praktik_budidaya',
|
||||
'skor_d4_sosial_ekonomi',
|
||||
'skor_d6_rantai_nilai_akses_pasar',
|
||||
'skor_komp_1_d4_k3_motivasi_kapasitas_adopsi',
|
||||
'skor_komp_2_d4_k2_ekonomi_rumah_tangga',
|
||||
'skor_komp_3_d2_praktik_budidaya_gap',
|
||||
'skor_komp_4_d4_k4_keterlibatan_kelembagaan',
|
||||
'skor_komp_5_d4_k5_k6_akses_kebebasan_keuangan',
|
||||
'skor_komp_6_d6_k1_k4_k5_rantai_nilai_akses_pasar',
|
||||
];
|
||||
$hasAssessment = $skorPetani !== null && collect($assessmentFields)->every(
|
||||
fn (string $field) => $skorPetani->{$field} !== null
|
||||
);
|
||||
$profiling = $hasAssessment ? $this->buildProfiling($skorPetani) : null;
|
||||
|
||||
$tanaman = Tanaman::where('petani_id', $petani->id)
|
||||
->where('komoditas_id', $petani->komoditas_id)
|
||||
->first();
|
||||
|
||||
if ($tanaman?->updated_at && $tanaman->umur_tanaman !== null) {
|
||||
$tahunInputTanaman = $tanaman->updated_at->year;
|
||||
$umurTanaman = (int) $tanaman->umur_tanaman;
|
||||
$tahunSekarang = now()->year;
|
||||
|
||||
$tahunTanam = $tahunInputTanaman - $umurTanaman;
|
||||
$umurTanaman = $tahunSekarang - $tahunTanam;
|
||||
} else {
|
||||
$tahunTanam = null;
|
||||
$umurTanaman = null;
|
||||
}
|
||||
|
||||
$praktikBudidaya = PraktikBudidaya::where('petani_id', $petani->id)->first();
|
||||
$surveyPanen = SurveyPanen::where('petani_id', $petani->id)->first();
|
||||
$sosialEkonomi = SosialEkonomi::where('petani_id', $id)->first();
|
||||
|
||||
$indikatorOnFarm = collect();
|
||||
|
||||
if ($praktikBudidaya) {
|
||||
|
||||
$indikatorOnFarm = collect([
|
||||
[
|
||||
'key' => 'pemangkasan',
|
||||
'nama' => 'Pemangkasan',
|
||||
'skor' => $praktikBudidaya->frekuensi_pemangkasan_utama,
|
||||
'deskripsi' => FrekuensiPemangkasanUtama::tryFrom(
|
||||
(int) $praktikBudidaya->frekuensi_pemangkasan_utama
|
||||
)?->label(),
|
||||
],
|
||||
|
||||
[
|
||||
'key' => 'pemupukan',
|
||||
'nama' => 'Pemupukan',
|
||||
'skor' => $praktikBudidaya->pemberian_pupuk,
|
||||
'deskripsi' => PemberianPupuk::tryFrom(
|
||||
(int) $praktikBudidaya->pemberian_pupuk
|
||||
)?->label(),
|
||||
],
|
||||
|
||||
[
|
||||
'key' => 'opt',
|
||||
'nama' => 'Hama / Penyakit',
|
||||
'skor' => $praktikBudidaya->metode_penanganan_opt,
|
||||
'deskripsi' => MetodePenangananHamaPenyakit::tryFrom(
|
||||
(int) $praktikBudidaya->metode_penanganan_opt
|
||||
)?->label(),
|
||||
],
|
||||
|
||||
[
|
||||
'key' => 'sanitasi',
|
||||
'nama' => 'Sanitasi Kebun',
|
||||
'skor' => $praktikBudidaya->frekuensi_sanitasi_lahan,
|
||||
'deskripsi' => FrekuensiSanitasiLahan::tryFrom(
|
||||
(int) $praktikBudidaya->frekuensi_sanitasi_lahan
|
||||
)?->label(),
|
||||
],
|
||||
|
||||
[
|
||||
'key' => 'gulma',
|
||||
'nama' => 'Penanganan Gulma',
|
||||
'skor' => $praktikBudidaya->penanganan_gulma,
|
||||
'deskripsi' => PenangananGulma::tryFrom(
|
||||
(int) $praktikBudidaya->penanganan_gulma
|
||||
)?->label(),
|
||||
],
|
||||
|
||||
[
|
||||
'key' => 'air',
|
||||
'nama' => 'Pengelolaan Air',
|
||||
'skor' => $praktikBudidaya->kondisi_pengelolaan_air,
|
||||
'deskripsi' => KondisiPengelolaanAir::tryFrom(
|
||||
(int) $praktikBudidaya->kondisi_pengelolaan_air
|
||||
)?->label(),
|
||||
],
|
||||
])
|
||||
->map(function ($item) {
|
||||
|
||||
$skor = $item['skor'] !== null
|
||||
? (int) $item['skor']
|
||||
: null;
|
||||
|
||||
return array_merge($item, [
|
||||
'skor' => $skor,
|
||||
'persentase' => $skor !== null
|
||||
? ($skor / 5) * 100
|
||||
: 0,
|
||||
|
||||
'color' => $this->getScoreColor($skor),
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
$detailOnFarm = [
|
||||
'jenis_hama' => $praktikBudidaya?->jenis_hama ?? [],
|
||||
'jenis_penyakit' => $praktikBudidaya?->jenis_penyakit ?? [],
|
||||
'konservasi_tanah' => $praktikBudidaya?->kebiasaan_konservasi_tanah ?? [],
|
||||
];
|
||||
|
||||
$d4SosialEkonomi = null;
|
||||
|
||||
$terdaftarKelompok = $sosialEkonomi?->terdaftar_kelompok_tani;
|
||||
$peranKelompok = $sosialEkonomi?->peran_dalam_kelompok;
|
||||
|
||||
if ($sosialEkonomi) {
|
||||
|
||||
$kecukupanPenghasilan = $sosialEkonomi->skor_kecukupan_penghasilan;
|
||||
$kemampuanMenabung = $sosialEkonomi->skor_kemampuan_menabung;
|
||||
|
||||
$rekening = $sosialEkonomi->status_rekening_tabungan;
|
||||
$kredit = $sosialEkonomi->pengalaman_kredit_kebun;
|
||||
$hutangPanen = $sosialEkonomi->keterikatan_hutang_panen;
|
||||
$ijon = $sosialEkonomi->praktik_ijon;
|
||||
|
||||
$programKeuanganRaw = $sosialEkonomi->status_program_keuangan_formal ?? [];
|
||||
$namaProgram = [
|
||||
'pkh' => 'PKH',
|
||||
'blt' => 'BLT',
|
||||
'kur' => 'KUR',
|
||||
'kredit_koperasi_resmi' => 'Kredit Koperasi',
|
||||
'credit_union_cu' => 'Credit Union (CU)',
|
||||
'pinjaman_bumdes' => 'Pinjaman BUMDes',
|
||||
'modal_hibah_dinas_pertanian' => 'Hibah Dinas Pertanian',
|
||||
];
|
||||
|
||||
$programBantuan = collect($programKeuanganRaw)->map(function ($status, $key) use ($namaProgram) {
|
||||
$statusMeta = match ($status) {
|
||||
'sedang_menerima' => [
|
||||
'label' => 'Sedang Menerima',
|
||||
'short' => 'Aktif',
|
||||
'color' => 'success',
|
||||
'icon' => 'tabler-circle-check',
|
||||
],
|
||||
'pernah_tapi_tidak_lagi' => [
|
||||
'label' => 'Pernah, Tidak Lagi',
|
||||
'short' => 'Pernah',
|
||||
'color' => 'info',
|
||||
'icon' => 'tabler-history',
|
||||
],
|
||||
'belum_pernah' => [
|
||||
'label' => 'Belum Pernah',
|
||||
'short' => 'Belum Pernah',
|
||||
'color' => 'secondary',
|
||||
'icon' => 'tabler-minus',
|
||||
],
|
||||
default => [
|
||||
'label' => filled($status) ? Str::headline($status) : 'Belum diisi',
|
||||
'short' => filled($status) ? Str::headline($status) : 'Belum diisi',
|
||||
'color' => 'secondary',
|
||||
'icon' => 'tabler-help-circle',
|
||||
],
|
||||
};
|
||||
|
||||
return [
|
||||
'key' => $key,
|
||||
'nama' => $namaProgram[$key] ?? Str::headline($key),
|
||||
'status' => $status,
|
||||
'label' => $statusMeta['label'],
|
||||
'short' => $statusMeta['short'],
|
||||
'color' => $statusMeta['color'],
|
||||
'icon' => $statusMeta['icon'],
|
||||
];
|
||||
|
||||
})
|
||||
->values();
|
||||
|
||||
$d4SosialEkonomi = [
|
||||
|
||||
'jumlah_tanggungan' => $sosialEkonomi->jumlah_tanggungan,
|
||||
|
||||
'kecukupan_penghasilan' => [
|
||||
'skor' => $kecukupanPenghasilan?->value,
|
||||
'label' => $kecukupanPenghasilan?->label(),
|
||||
|
||||
'color' => $this->getScoreColor(
|
||||
$kecukupanPenghasilan?->value
|
||||
),
|
||||
],
|
||||
|
||||
'kemampuan_menabung' => [
|
||||
'skor' => $kemampuanMenabung?->value,
|
||||
'label' => $kemampuanMenabung?->label(),
|
||||
|
||||
'color' => $this->getScoreColor(
|
||||
$kemampuanMenabung?->value
|
||||
),
|
||||
],
|
||||
|
||||
'status_kepemilikan_lahan' => $sosialEkonomi->status_kepemilikan_lahan,
|
||||
|
||||
'kelembagaan' => [
|
||||
'terdaftar' =>
|
||||
$terdaftarKelompok === TerdaftarKelompokTani::YA,
|
||||
|
||||
'status' =>
|
||||
$terdaftarKelompok?->label(),
|
||||
|
||||
'peran' =>
|
||||
$peranKelompok?->label(),
|
||||
],
|
||||
|
||||
'kondisi_keuangan' => [
|
||||
'rekening' => [
|
||||
'label' => $rekening?->label(),
|
||||
'value' => $rekening?->value,
|
||||
'color' => $this->getFinancialColor(
|
||||
$rekening?->value
|
||||
),
|
||||
'icon' => 'tabler-building-bank',
|
||||
],
|
||||
|
||||
'kredit' => [
|
||||
'label' => $kredit?->label(),
|
||||
'value' => $kredit?->value,
|
||||
'color' => $this->getFinancialColor(
|
||||
$kredit?->value
|
||||
),
|
||||
'icon' => 'tabler-credit-card',
|
||||
],
|
||||
|
||||
'hutang_panen' => [
|
||||
'label' => $hutangPanen?->label(),
|
||||
'value' => $hutangPanen?->value,
|
||||
'color' => $this->getFinancialColor(
|
||||
$hutangPanen?->value
|
||||
),
|
||||
'icon' => 'tabler-coins',
|
||||
],
|
||||
|
||||
'ijon' => [
|
||||
'label' => $ijon?->label(),
|
||||
'value' => $ijon?->value,
|
||||
'color' => $this->getFinancialColor(
|
||||
$ijon?->value
|
||||
),
|
||||
'icon' => 'tabler-link',
|
||||
],
|
||||
],
|
||||
|
||||
'program_bantuan' => $programBantuan,
|
||||
];
|
||||
}
|
||||
|
||||
$surveyPelatihan = SurveyPelatihan::where('petani_id', $id)
|
||||
->orderByDesc('tahun_pelaksanaan')
|
||||
->get();
|
||||
|
||||
$surveySertifikasi = SurveySertifikasi::where('petani_id', $id)
|
||||
->orderByDesc('tahun_pelaksanaan')
|
||||
->get();
|
||||
|
||||
$aksesPasar = AksesPasar::where('petani_id', $id)->first();
|
||||
$saluranTerdahulu = collect($aksesPasar?->saluran_terdahulu ?? [] );
|
||||
|
||||
$d6SaluranPasar = [
|
||||
[
|
||||
'key' => 'tengkulak',
|
||||
'label' => 'Tengkulak',
|
||||
'icon' => 'tabler-user-dollar',
|
||||
'aktif' => $saluranTerdahulu->contains(
|
||||
fn ($item) => Str::contains(
|
||||
Str::lower($item),
|
||||
['tengkulak', 'pedagang lokal']
|
||||
)
|
||||
),
|
||||
],
|
||||
|
||||
[
|
||||
'key' => 'koperasi',
|
||||
'label' => 'Koperasi',
|
||||
'icon' => 'tabler-users-group',
|
||||
'aktif' => $saluranTerdahulu->contains(
|
||||
fn ($item) => Str::contains(
|
||||
Str::lower($item),
|
||||
'koperasi'
|
||||
)
|
||||
),
|
||||
],
|
||||
|
||||
[
|
||||
'key' => 'bumdes',
|
||||
'label' => 'BUMDes',
|
||||
'icon' => 'tabler-building-community',
|
||||
'aktif' => $saluranTerdahulu->contains(
|
||||
fn ($item) => Str::contains(
|
||||
Str::lower($item),
|
||||
'bumdes'
|
||||
)
|
||||
),
|
||||
],
|
||||
|
||||
[
|
||||
'key' => 'eksportir',
|
||||
'label' => 'Eksportir',
|
||||
'icon' => 'tabler-plane-departure',
|
||||
'aktif' => $saluranTerdahulu->contains(
|
||||
fn ($item) => Str::contains(
|
||||
Str::lower($item),
|
||||
'eksportir'
|
||||
)
|
||||
),
|
||||
],
|
||||
];
|
||||
|
||||
$hargaJualPanen = HargaJualPanen::where('petani_id', $id)->get();
|
||||
|
||||
$d6HargaJual = $hargaJualPanen->map(function ($item) {
|
||||
|
||||
$hargaSekarang = $item->harga_panen_terakhir !== null
|
||||
? (float) $item->harga_panen_terakhir
|
||||
: null;
|
||||
|
||||
$hargaTahunLalu = $item->harga_tahun_lalu !== null
|
||||
? (float) $item->harga_tahun_lalu
|
||||
: null;
|
||||
|
||||
$perubahan = null;
|
||||
|
||||
if (
|
||||
$hargaSekarang !== null &&
|
||||
$hargaTahunLalu !== null &&
|
||||
$hargaTahunLalu > 0
|
||||
) {
|
||||
$perubahan = (
|
||||
($hargaSekarang - $hargaTahunLalu)
|
||||
/ $hargaTahunLalu
|
||||
) * 100;
|
||||
}
|
||||
|
||||
return [
|
||||
'bentuk_produk' => $item->bentuk_produk,
|
||||
|
||||
'harga_sekarang' => $hargaSekarang,
|
||||
|
||||
'harga_tahun_lalu' => $hargaTahunLalu,
|
||||
|
||||
'harga_dua_tahun_lalu' =>
|
||||
$item->harga_dua_tahun_lalu !== null
|
||||
? (float) $item->harga_dua_tahun_lalu
|
||||
: null,
|
||||
|
||||
'perubahan' => $perubahan,
|
||||
|
||||
'trend' => match (true) {
|
||||
$perubahan === null => 'neutral',
|
||||
$perubahan > 0 => 'up',
|
||||
$perubahan < 0 => 'down',
|
||||
default => 'stable',
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
$kebebasanPembeli = $sosialEkonomi?->kebebasan_memilih_pembeli;
|
||||
|
||||
$d6RantaiNilai = [
|
||||
'saluran_pasar' => $d6SaluranPasar,
|
||||
|
||||
'harga_jual' => $d6HargaJual,
|
||||
|
||||
'kebebasan_memilih_pembeli' => [
|
||||
'skor' => $kebebasanPembeli?->value,
|
||||
'label' => $kebebasanPembeli?->label(),
|
||||
|
||||
'persentase' => $kebebasanPembeli
|
||||
? ($kebebasanPembeli->value / 5) * 100
|
||||
: 0,
|
||||
|
||||
'color' => $this->getScoreColor(
|
||||
$kebebasanPembeli?->value
|
||||
),
|
||||
],
|
||||
];
|
||||
|
||||
// Report missing profile data without treating an unanswered field as zero.
|
||||
$missingSections = [];
|
||||
$identityFields = [
|
||||
'nama' => 'Nama', 'ktp' => 'NIK', 'tanggal_lahir' => 'Tanggal lahir',
|
||||
'jenis_kelamin' => 'Jenis kelamin', 'pendidikan_terakhir' => 'Pendidikan terakhir',
|
||||
'status_perkawinan' => 'Status perkawinan',
|
||||
];
|
||||
foreach ($identityFields as $field => $label) {
|
||||
if (blank($petani->{$field})) {
|
||||
$missingSections[] = $label;
|
||||
}
|
||||
}
|
||||
if (!$desaKelurahan?->nama || !$desaKelurahan?->kecamatan?->nama) {
|
||||
$missingSections[] = 'Wilayah desa/kecamatan';
|
||||
}
|
||||
if (!$komoditasUtama?->nama) {
|
||||
$missingSections[] = 'Komoditas utama';
|
||||
}
|
||||
if ($lahan->isEmpty()) {
|
||||
$missingSections[] = 'Data lahan';
|
||||
}
|
||||
if ($tahunTanam === null) {
|
||||
$missingSections[] = 'Umur tanaman';
|
||||
}
|
||||
if (!$praktikBudidaya || $indikatorOnFarm->contains(fn ($item) => $item['skor'] === null)) {
|
||||
$missingSections[] = 'Praktik budidaya';
|
||||
}
|
||||
if ($surveyPanen?->hasil_komoditas_utama_permusim === null) {
|
||||
$missingSections[] = 'Survei panen';
|
||||
}
|
||||
$socialFields = [
|
||||
'jumlah_tanggungan', 'skor_kecukupan_penghasilan', 'skor_kemampuan_menabung',
|
||||
'status_kepemilikan_lahan', 'terdaftar_kelompok_tani', 'status_rekening_tabungan',
|
||||
'pengalaman_kredit_kebun', 'keterikatan_hutang_panen', 'praktik_ijon',
|
||||
'kebebasan_memilih_pembeli',
|
||||
];
|
||||
if (!$sosialEkonomi || collect($socialFields)->contains(fn ($field) => blank($sosialEkonomi->{$field}))) {
|
||||
$missingSections[] = 'Sosial ekonomi';
|
||||
}
|
||||
if (!$aksesPasar || blank($aksesPasar->saluran_terdahulu)) {
|
||||
$missingSections[] = 'Akses pasar';
|
||||
}
|
||||
if (!$hasAssessment) {
|
||||
$missingSections[] = 'Penilaian kesiapan adopsi belum lengkap';
|
||||
}
|
||||
|
||||
$data = [
|
||||
'missingSections' => $missingSections,
|
||||
'hasAssessment' => $hasAssessment,
|
||||
'petani' => $petani,
|
||||
'skorPetani' => $skorPetani,
|
||||
'enumerator' => $enumerator,
|
||||
'komoditasUtama' => $komoditasUtama,
|
||||
'lahan' => $lahan,
|
||||
'farmerLandMap' => $this->farmerLandMapData($lahan),
|
||||
'desaKelurahan' => $desaKelurahan,
|
||||
'ktpMasked' => $ktpMasked,
|
||||
'profiling' => $profiling,
|
||||
'tahunTanam' => $tahunTanam,
|
||||
'umurTanaman' => $umurTanaman,
|
||||
'surveyPanen' => $surveyPanen,
|
||||
'praktikBudidaya' => $praktikBudidaya,
|
||||
'indikatorOnFarm' => $indikatorOnFarm,
|
||||
'detailOnFarm' => $detailOnFarm,
|
||||
'sosialEkonomi' => $sosialEkonomi,
|
||||
'd4SosialEkonomi' => $d4SosialEkonomi,
|
||||
'surveyPelatihan' => $surveyPelatihan,
|
||||
'surveySertifikasi' => $surveySertifikasi,
|
||||
'aksesPasar' => $aksesPasar,
|
||||
'd6SaluranPasar' => $d6SaluranPasar,
|
||||
'hargaJualPanen' => $hargaJualPanen,
|
||||
'd6HargaJual' => $d6HargaJual,
|
||||
'kebebasanPembeli' => $kebebasanPembeli,
|
||||
'd6RantaiNilai' => $d6RantaiNilai
|
||||
];
|
||||
return view('produktivitas.profil-petani.index', $data);
|
||||
}
|
||||
|
||||
private function farmerLandMapData(\Illuminate\Support\Collection $lands): array
|
||||
{
|
||||
$regions = ['desa' => [], 'kecamatan' => []];
|
||||
$plots = [];
|
||||
|
||||
foreach ($lands as $land) {
|
||||
$plot = [
|
||||
'id' => $land->id,
|
||||
'nama' => $land->nama,
|
||||
'path' => $land->path,
|
||||
'luas' => $land->luas_lahan,
|
||||
'komoditas' => $land->komoditas?->nama,
|
||||
];
|
||||
foreach (['desa' => $land->desaKelurahan, 'kecamatan' => $land->desaKelurahan?->kecamatan] as $level => $region) {
|
||||
$plot[$level . '_id'] = $region?->id;
|
||||
if ($region && !isset($regions[$level][$region->id])) {
|
||||
$regions[$level][$region->id] = [
|
||||
'id' => $region->id,
|
||||
'nama' => $region->nama,
|
||||
'path' => $region->path,
|
||||
];
|
||||
}
|
||||
}
|
||||
$plots[] = $plot;
|
||||
}
|
||||
|
||||
return ['lands' => $plots, 'regions' => $regions];
|
||||
}
|
||||
|
||||
private function getScoreColor(?int $score): string
|
||||
{
|
||||
if ($score === null) {
|
||||
return 'secondary';
|
||||
}
|
||||
|
||||
return match (true) {
|
||||
$score >= 5 => 'success',
|
||||
$score >= 4 => 'info',
|
||||
$score >= 3 => 'primary',
|
||||
$score >= 2 => 'warning',
|
||||
default => 'danger',
|
||||
};
|
||||
}
|
||||
|
||||
private function getFinancialColor(?int $score): string
|
||||
{
|
||||
if ($score === null || $score === 0) {
|
||||
return 'secondary';
|
||||
}
|
||||
|
||||
return match (true) {
|
||||
$score >= 5 => 'success',
|
||||
$score >= 4 => 'info',
|
||||
$score >= 2 => 'warning',
|
||||
default => 'danger',
|
||||
};
|
||||
}
|
||||
|
||||
private function buildProfiling(SkorPetani $skorPetani): array
|
||||
{
|
||||
$skorAkhir = $this->normalizeScore($skorPetani->skor_kesiapan_adopsi);
|
||||
$segmen = $this->getSegmen($skorAkhir);
|
||||
|
||||
$dimensions = [
|
||||
'd2' => [
|
||||
'kode' => 'D2',
|
||||
'nama' => 'Sistem & Praktik Budidaya',
|
||||
'skor' => $this->normalizeScore(
|
||||
$skorPetani->skor_d2_sistem_praktik_budidaya
|
||||
),
|
||||
],
|
||||
|
||||
'd4' => [
|
||||
'kode' => 'D4',
|
||||
'nama' => 'Sosial-Ekonomi Rumah Tangga',
|
||||
'skor' => $this->normalizeScore(
|
||||
$skorPetani->skor_d4_sosial_ekonomi
|
||||
),
|
||||
],
|
||||
|
||||
'd6' => [
|
||||
'kode' => 'D6',
|
||||
'nama' => 'Rantai Nilai & Akses Pasar',
|
||||
'skor' => $this->normalizeScore(
|
||||
$skorPetani->skor_d6_rantai_nilai_akses_pasar
|
||||
),
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($dimensions as $key => $dimension) {
|
||||
$dimensions[$key]['level'] = $this->getDimensionLevel($dimension['skor']);
|
||||
}
|
||||
|
||||
$components = collect([
|
||||
[
|
||||
'key' => 'motivasi',
|
||||
'nama' => 'Motivasi & Kapasitas Adopsi',
|
||||
'short' => 'Motivasi',
|
||||
'dimensi' => 'D4',
|
||||
'bobot' => 25,
|
||||
'icon' => 'tabler-bulb',
|
||||
'skor' => $this->normalizeScore($skorPetani->skor_komp_1_d4_k3_motivasi_kapasitas_adopsi),
|
||||
],
|
||||
|
||||
[
|
||||
'key' => 'ekonomi',
|
||||
'nama' => 'Kondisi Ekonomi Rumah Tangga',
|
||||
'short' => 'Ekonomi',
|
||||
'dimensi' => 'D4',
|
||||
'bobot' => 20,
|
||||
'icon' => 'tabler-home-dollar',
|
||||
'skor' => $this->normalizeScore($skorPetani->skor_komp_2_d4_k2_ekonomi_rumah_tangga),
|
||||
],
|
||||
|
||||
[
|
||||
'key' => 'gap',
|
||||
'nama' => 'Praktik Budidaya & GAP',
|
||||
'short' => 'Budidaya / GAP',
|
||||
'dimensi' => 'D2',
|
||||
'bobot' => 20,
|
||||
'icon' => 'tabler-plant-2',
|
||||
'skor' => $this->normalizeScore($skorPetani->skor_komp_3_d2_praktik_budidaya_gap),
|
||||
],
|
||||
|
||||
[
|
||||
'key' => 'kelembagaan',
|
||||
'nama' => 'Keterlibatan Kelembagaan',
|
||||
'short' => 'Kelembagaan',
|
||||
'dimensi' => 'D4',
|
||||
'bobot' => 15,
|
||||
'icon' => 'tabler-users-group',
|
||||
'skor' => $this->normalizeScore($skorPetani->skor_komp_4_d4_k4_keterlibatan_kelembagaan),
|
||||
],
|
||||
|
||||
[
|
||||
'key' => 'keuangan',
|
||||
'nama' => 'Akses & Kebebasan Keuangan',
|
||||
'short' => 'Keuangan',
|
||||
'dimensi' => 'D4',
|
||||
'bobot' => 10,
|
||||
'icon' => 'tabler-building-bank',
|
||||
'skor' => $this->normalizeScore($skorPetani->skor_komp_5_d4_k5_k6_akses_kebebasan_keuangan),
|
||||
],
|
||||
|
||||
[
|
||||
'key' => 'pasar',
|
||||
'nama' => 'Rantai Nilai & Akses Pasar',
|
||||
'short' => 'Akses Pasar',
|
||||
'dimensi' => 'D6',
|
||||
'bobot' => 10,
|
||||
'icon' => 'tabler-building-store',
|
||||
'skor' => $this->normalizeScore($skorPetani->skor_komp_6_d6_k1_k4_k5_rantai_nilai_akses_pasar),
|
||||
],
|
||||
]);
|
||||
|
||||
$availableComponents = $components
|
||||
->filter(fn ($item) => $item['skor'] !== null)
|
||||
->values();
|
||||
|
||||
$strengths = $availableComponents
|
||||
->sortByDesc('skor')
|
||||
->take(2)
|
||||
->values();
|
||||
|
||||
$barriers = $availableComponents
|
||||
->filter(fn ($item) => $item['skor'] < 3)
|
||||
->sortBy('skor')
|
||||
->values();
|
||||
|
||||
$specialPattern = $this->detectSpecialPattern(
|
||||
$segmen,
|
||||
$dimensions,
|
||||
$skorPetani
|
||||
);
|
||||
|
||||
$title = $this->buildProfileTitle(
|
||||
$segmen,
|
||||
$specialPattern,
|
||||
$barriers
|
||||
);
|
||||
|
||||
$narrative = $this->buildNarrative(
|
||||
$segmen,
|
||||
$specialPattern,
|
||||
$strengths,
|
||||
$barriers
|
||||
);
|
||||
|
||||
$recommendations = $this->buildRecommendations(
|
||||
$specialPattern,
|
||||
$barriers
|
||||
);
|
||||
|
||||
return [
|
||||
'skor_akhir' => $skorAkhir,
|
||||
'segmen' => $segmen,
|
||||
'title' => $title,
|
||||
'dimensions' => $dimensions,
|
||||
'components' => $components,
|
||||
'strengths' => $strengths,
|
||||
'barriers' => $barriers,
|
||||
'special_pattern' => $specialPattern,
|
||||
'narrative' => $narrative,
|
||||
'recommendations' => $recommendations,
|
||||
];
|
||||
}
|
||||
|
||||
private function normalizeScore($score): ?float
|
||||
{
|
||||
if ($score === null || $score === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$score = (float) $score;
|
||||
|
||||
if ($score <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $score;
|
||||
}
|
||||
|
||||
private function getSegmen(?float $score): string
|
||||
{
|
||||
if ($score === null) {
|
||||
return 'Belum Dinilai';
|
||||
}
|
||||
|
||||
return match (true) {
|
||||
$score >= 4.0 => 'Penggerak',
|
||||
$score >= 3.0 => 'Perintis',
|
||||
$score >= 2.0 => 'Pemerhati',
|
||||
default => 'Pemula',
|
||||
};
|
||||
}
|
||||
|
||||
private function getDimensionLevel(?float $score): string
|
||||
{
|
||||
if ($score === null) {
|
||||
return 'belum_dinilai';
|
||||
}
|
||||
|
||||
if ($score <= self::DIMENSION_LOW_MAX) {
|
||||
return 'rendah';
|
||||
}
|
||||
|
||||
if ($score <= self::DIMENSION_MEDIUM_MAX) {
|
||||
return 'sedang';
|
||||
}
|
||||
|
||||
return 'tinggi';
|
||||
}
|
||||
|
||||
private function detectSpecialPattern(
|
||||
string $segmen,
|
||||
array $dimensions,
|
||||
SkorPetani $skorPetani
|
||||
): ?array {
|
||||
|
||||
$d2 = $dimensions['d2']['level'];
|
||||
$d4 = $dimensions['d4']['level'];
|
||||
$d6 = $dimensions['d6']['level'];
|
||||
|
||||
if (
|
||||
$segmen === 'Perintis' &&
|
||||
$d4 === 'tinggi' &&
|
||||
$d2 === 'rendah' &&
|
||||
$d6 === 'sedang'
|
||||
) {
|
||||
return [
|
||||
'kode' => 'perintis_teknis',
|
||||
'nama' => 'Perintis dengan Hambatan Teknis',
|
||||
'prioritas' => 'gap',
|
||||
];
|
||||
}
|
||||
|
||||
if (
|
||||
$segmen === 'Perintis' &&
|
||||
$d4 === 'tinggi' &&
|
||||
in_array($d2, ['sedang', 'tinggi']) && $d6 === 'rendah'
|
||||
) {
|
||||
return [
|
||||
'kode' => 'perintis_pasar',
|
||||
'nama' => 'Perintis dengan Hambatan Pasar',
|
||||
'prioritas' => 'pasar',
|
||||
];
|
||||
}
|
||||
|
||||
if (
|
||||
$segmen === 'Pemerhati' &&
|
||||
$d4 === 'sedang' &&
|
||||
$d2 === 'rendah' &&
|
||||
$d6 === 'sedang'
|
||||
) {
|
||||
return [
|
||||
'kode' => 'pemerhati_teknis',
|
||||
'nama' => 'Pemerhati Teknis',
|
||||
'prioritas' => 'gap',
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
if (
|
||||
$segmen === 'Pemula' &&
|
||||
$d4 === 'rendah' &&
|
||||
$d2 === 'rendah' &&
|
||||
$d6 === 'rendah'
|
||||
) {
|
||||
return [
|
||||
'kode' => 'pemula_struktural',
|
||||
'nama' => 'Pemula Struktural',
|
||||
'prioritas' => 'struktural',
|
||||
];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private function buildProfileTitle(
|
||||
string $segmen,
|
||||
?array $specialPattern,
|
||||
$barriers
|
||||
): string {
|
||||
if ($specialPattern) {
|
||||
return $specialPattern['nama'];
|
||||
}
|
||||
|
||||
if ($barriers->isNotEmpty()) {
|
||||
$mainBarrier = $barriers->first();
|
||||
return $segmen . ' dengan Prioritas ' . $mainBarrier['short'];
|
||||
}
|
||||
|
||||
return $segmen . ' dengan Profil Relatif Seimbang';
|
||||
}
|
||||
|
||||
private function buildNarrative(
|
||||
string $segmen,
|
||||
?array $specialPattern,
|
||||
$strengths,
|
||||
$barriers
|
||||
): string {
|
||||
if ($specialPattern) {
|
||||
return match ($specialPattern['kode']) {
|
||||
'perintis_teknis' =>
|
||||
'Petani memiliki fondasi sosial-ekonomi yang relatif siap '
|
||||
. 'untuk berkembang, tetapi penerapannya belum sepenuhnya '
|
||||
. 'tercermin dalam praktik budidaya. Hambatan utama berada '
|
||||
. 'pada aspek teknis di lapangan.',
|
||||
|
||||
'perintis_pasar' =>
|
||||
'Petani memiliki fondasi sosial-ekonomi dan kesiapan '
|
||||
. 'budidaya yang relatif baik, tetapi manfaat ekonominya '
|
||||
. 'masih dibatasi oleh rantai nilai dan akses pasar.',
|
||||
|
||||
'pemerhati_teknis' =>
|
||||
'Petani telah memiliki sebagian fondasi untuk berkembang, '
|
||||
. 'namun praktik budidaya masih menjadi hambatan utama. '
|
||||
. 'Pendekatan berbasis demonstrasi hasil nyata akan lebih '
|
||||
. 'relevan sebelum intervensi teknis yang lebih lanjut.',
|
||||
|
||||
'pemula_struktural' =>
|
||||
'Petani menghadapi hambatan pada beberapa dimensi sekaligus. '
|
||||
. 'Kondisi sosial-ekonomi, praktik budidaya, serta akses '
|
||||
. 'pasar perlu dibaca bersama untuk menentukan hambatan '
|
||||
. 'mendasar yang paling mendesak.',
|
||||
|
||||
default =>
|
||||
$this->buildGenericNarrative($segmen, $strengths, $barriers),
|
||||
};
|
||||
}
|
||||
|
||||
return $this->buildGenericNarrative($segmen, $strengths, $barriers);
|
||||
}
|
||||
|
||||
private function buildGenericNarrative(
|
||||
string $segmen,
|
||||
$strengths,
|
||||
$barriers
|
||||
): string {
|
||||
|
||||
$text = 'Petani berada pada segmen ' . $segmen . '. ';
|
||||
if ($strengths->isNotEmpty()) {
|
||||
$strengthNames = $strengths->pluck('nama')->implode(' dan ');
|
||||
$text .= 'Kekuatan relatif terlihat pada ' . $strengthNames . '. ';
|
||||
}
|
||||
|
||||
if ($barriers->isNotEmpty()) {
|
||||
$barrierNames = $barriers->take(3)->pluck('nama')->implode(', ');
|
||||
$text .=
|
||||
'Area yang masih memerlukan perhatian terutama pada ' . $barrierNames . '. Prioritas intervensi sebaiknya mengikuti ' . 'hambatan dengan skor paling rendah.';
|
||||
} else {
|
||||
$text .= 'Tidak terdapat komponen dengan skor rendah yang ' . 'menonjol. Pendampingan dapat difokuskan pada ' . 'pemeliharaan dan penguatan capaian yang sudah ada.';
|
||||
}
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
private function buildRecommendations(
|
||||
?array $specialPattern,
|
||||
$barriers
|
||||
): array {
|
||||
if ($specialPattern) {
|
||||
$specialRecommendation = match ($specialPattern['kode']) {
|
||||
'perintis_teknis' => [
|
||||
'Prioritaskan pendampingan GAP langsung di lapangan.',
|
||||
'Perkuat koneksi petani terhadap sumber input dan pelatihan teknis.',
|
||||
],
|
||||
'perintis_pasar' => [
|
||||
'Perkuat koneksi ke saluran pasar yang lebih baik.',
|
||||
'Fasilitasi akses pembeli, negosiasi, atau penguatan koperasi.',
|
||||
],
|
||||
'pemerhati_teknis' => [
|
||||
'Gunakan demonstrasi lapangan untuk menunjukkan hasil nyata.',
|
||||
'Lakukan pendampingan teknis secara bertahap sebelum pelatihan formal.',
|
||||
],
|
||||
'pemula_struktural' => [
|
||||
'Identifikasi hambatan dasar seperti lahan, hutang, atau logistik.',
|
||||
'Tangani hambatan struktural sebelum mendorong adopsi praktik baru.',
|
||||
],
|
||||
default => [],
|
||||
};
|
||||
if (!empty($specialRecommendation)) {
|
||||
return $specialRecommendation;
|
||||
}
|
||||
}
|
||||
$recommendationMap = [
|
||||
'motivasi' => 'Gunakan demonstrasi hasil nyata, role model, dan pendekatan perubahan secara bertahap.',
|
||||
'ekonomi' => 'Perkuat ruang gerak ekonomi rumah tangga sebelum mendorong investasi atau perubahan praktik yang berisiko.',
|
||||
'gap' => 'Prioritaskan pendampingan praktik budidaya dan GAP langsung di kebun.',
|
||||
'kelembagaan' => 'Perkuat keterlibatan petani dalam kelompok tani sebagai ruang belajar dan penguatan posisi tawar.',
|
||||
'keuangan' => 'Tinjau akses keuangan formal serta kemungkinan ikatan hutang panen atau ijon yang membatasi ruang gerak petani.',
|
||||
'pasar' => 'Perkuat koneksi dengan pembeli dan saluran pasar alternatif serta kemampuan negosiasi petani.',
|
||||
];
|
||||
|
||||
return $barriers->take(3)->map(
|
||||
fn ($item) =>
|
||||
$recommendationMap[$item['key']] ?? null
|
||||
)->filter()->values()->all();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -64,12 +64,26 @@ class ProfileController extends Controller
|
||||
$validator = Validator::make($request->all(), [
|
||||
'nama' => ['required', 'string', 'max:255'],
|
||||
'email' => ['required', 'email', 'max:255'],
|
||||
'telepon' => ['required', 'max:13'],
|
||||
'desa_kelurahan_id' => ['required'],
|
||||
'telepon' => ['nullable', 'max:13'],
|
||||
'desa_kelurahan_id' => ['nullable', 'exists:master_desa_kelurahan,id'],
|
||||
'ktp' => ['nullable', 'min:16', 'max:16'],
|
||||
'file_ktp' => ['nullable', 'file', 'mimes:jpeg,jpg,png,pdf', 'max:2048'],
|
||||
'kk' => ['nullable', 'min:16', 'max:16'],
|
||||
'file_kk' => ['nullable', 'file', 'mimes:jpeg,jpg,png,pdf', 'max:2048'],
|
||||
], [
|
||||
'nama.required' => 'Nama wajib diisi.',
|
||||
'email.required' => 'Email wajib diisi.',
|
||||
'email.email' => 'Format email tidak sesuai.',
|
||||
'telepon.max' => 'Nomor telepon tidak sesuai.',
|
||||
'desa_kelurahan_id.exists' => 'Desa kelurahan tidak ditemukan.',
|
||||
'ktp.min' => 'KTP harus terdiri dari 16 digit.',
|
||||
'ktp.max' => 'KTP harus terdiri dari 16 digit.',
|
||||
'kk.min' => 'KK harus terdiri dari 16 digit.',
|
||||
'kk.max' => 'KK harus terdiri dari 16 digit.',
|
||||
'file_ktp.mimes' => 'Format file harus dalam bentuk JPEG, JPG, PNG, atau PDF.',
|
||||
'file_kk.mimes' => 'Format file harus dalam bentuk JPEG, JPG, PNG, atau PDF.',
|
||||
'file_ktp.max' => 'Ukuran file maksimal 2MB.',
|
||||
'file_kk.max' => 'Ukuran file maksimal 2MB.',
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
@@ -143,12 +157,31 @@ class ProfileController extends Controller
|
||||
$validator = Validator::make($request->all(), [
|
||||
'nama' => ['required', 'string', 'max:255'],
|
||||
'email' => ['required', 'email', 'max:255'],
|
||||
'telepon' => ['required', 'max:13'],
|
||||
'desa_kelurahan_id' => ['required'],
|
||||
'telepon' => ['nullable', 'max:13'],
|
||||
'desa_kelurahan_id' => ['nullable', 'exists:master_desa_kelurahan,id'],
|
||||
'ktp' => ['nullable', 'min:16', 'max:16'],
|
||||
'file_ktp' => ['nullable', 'file', 'mimes:jpeg,jpg,png,pdf', 'max:2048'],
|
||||
'kk' => ['nullable', 'min:16', 'max:16'],
|
||||
'file_kk' => ['nullable', 'file', 'mimes:jpeg,jpg,png,pdf', 'max:2048'],
|
||||
], [
|
||||
'nama.required' => 'Nama harus diisi.',
|
||||
'nama.string' => 'Nama harus berupa string.',
|
||||
'nama.max' => 'Nama tidak boleh lebih dari 255 karakter.',
|
||||
'email.required' => 'Email harus diisi.',
|
||||
'email.email' => 'Email harus berupa alamat email yang valid.',
|
||||
'email.max' => 'Email tidak boleh lebih dari 255 karakter.',
|
||||
'telepon.max' => 'Nomor telepon tidak boleh lebih dari 13 karakter.',
|
||||
'desa_kelurahan_id.exists' => 'Desa kelurahan tidak ditemukan.',
|
||||
'ktp.min' => 'Nomor KTP harus terdiri dari 16 karakter.',
|
||||
'ktp.max' => 'Nomor KTP harus terdiri dari 16 karakter.',
|
||||
'kk.min' => 'Nomor KK harus terdiri dari 16 karakter.',
|
||||
'kk.max' => 'Nomor KK harus terdiri dari 16 karakter.',
|
||||
'file_ktp.file' => 'File harus berupa file.',
|
||||
'file_ktp.mimes' => 'File harus berupa file dengan ekstensi jpeg, jpg, png, atau pdf.',
|
||||
'file_ktp.max' => 'Ukuran file tidak boleh lebih dari 2MB.',
|
||||
'file_kk.file' => 'File harus berupa file.',
|
||||
'file_kk.mimes' => 'File harus berupa file dengan ekstensi jpeg, jpg, png, atau pdf.',
|
||||
'file_kk.max' => 'Ukuran file tidak boleh lebih dari 2MB.',
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
|
||||
@@ -154,8 +154,7 @@ class SosialEkonomiController extends Controller
|
||||
});
|
||||
|
||||
if (!$request->expectsJson()) {
|
||||
return redirect()
|
||||
->route('petani.sosial-ekonomi.edit', [$sosialEkonomi->petani_id, $sosialEkonomi->id])
|
||||
return $this->redirectToEditSection($sosialEkonomi, $request)
|
||||
->with('success', 'Data sosial ekonomi berhasil disimpan.');
|
||||
}
|
||||
|
||||
@@ -206,8 +205,7 @@ class SosialEkonomiController extends Controller
|
||||
});
|
||||
|
||||
if (!$request->expectsJson()) {
|
||||
return redirect()
|
||||
->route('petani.sosial-ekonomi.edit', [$sosialEkonomi->petani_id, $sosialEkonomi->id])
|
||||
return $this->redirectToEditSection($sosialEkonomi, $request)
|
||||
->with('success', 'Data sosial ekonomi berhasil diperbarui.');
|
||||
}
|
||||
|
||||
@@ -218,6 +216,18 @@ class SosialEkonomiController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
private function redirectToEditSection(SosialEkonomi $sosialEkonomi, Request $request): RedirectResponse
|
||||
{
|
||||
$url = route('petani.sosial-ekonomi.edit', [$sosialEkonomi->petani_id, $sosialEkonomi->id]);
|
||||
$fragment = $request->input('redirect_fragment');
|
||||
|
||||
if (is_string($fragment) && preg_match('/^[A-Za-z0-9_-]+$/', $fragment)) {
|
||||
$url .= '#' . $fragment;
|
||||
}
|
||||
|
||||
return redirect()->to($url);
|
||||
}
|
||||
|
||||
public function destroy(string $id): JsonResponse
|
||||
{
|
||||
$sosialEkonomi = SosialEkonomi::findOrFail($id);
|
||||
@@ -388,15 +398,17 @@ class SosialEkonomiController extends Controller
|
||||
}
|
||||
|
||||
if ($section === 'keanggotaan_kelompok') {
|
||||
$terdaftarKelompokTani = $request->input('terdaftar_kelompok_tani') === TerdaftarKelompokTani::YA->value;
|
||||
|
||||
$rules = array_merge($rules, [
|
||||
'terdaftar_kelompok_tani' => ['nullable', 'string', Rule::in(TerdaftarKelompokTani::values())],
|
||||
'nama_kelompok_tani' => ['required_if:terdaftar_kelompok_tani,' . TerdaftarKelompokTani::YA->value, 'nullable', 'string', 'max:255'],
|
||||
'lama_bergabung_kelompok_tahun' => ['required_if:terdaftar_kelompok_tani,' . TerdaftarKelompokTani::YA->value, 'nullable', 'integer', 'min:0'],
|
||||
'kedekatan_sesama_anggota' => ['required_if:terdaftar_kelompok_tani,' . TerdaftarKelompokTani::YA->value, 'nullable', 'string', Rule::in(KedekatanSesamaAnggota::values())],
|
||||
'keaktifan_pertemuan_kelompok' => ['required', 'integer', Rule::in(KeaktifanPertemuanKelompok::values())],
|
||||
'topik_biasa_dibahas' => ['required', 'string'],
|
||||
'peran_dalam_kelompok' => ['required', 'integer', Rule::in(PeranDalamKelompok::values())],
|
||||
'manfaat_kelompok_tani' => ['required', 'integer', Rule::in(ManfaatKelompokTani::values())],
|
||||
'keaktifan_pertemuan_kelompok' => [Rule::requiredIf($terdaftarKelompokTani), 'nullable', 'integer', Rule::in(KeaktifanPertemuanKelompok::values())],
|
||||
'topik_biasa_dibahas' => [Rule::requiredIf($terdaftarKelompokTani), 'nullable', 'string'],
|
||||
'peran_dalam_kelompok' => [Rule::requiredIf($terdaftarKelompokTani), 'nullable', 'integer', Rule::in(PeranDalamKelompok::values())],
|
||||
'manfaat_kelompok_tani' => [Rule::requiredIf($terdaftarKelompokTani), 'nullable', 'integer', Rule::in(ManfaatKelompokTani::values())],
|
||||
'status_kartu_tani' => ['nullable', 'string', Rule::in(StatusKartuTani::values())],
|
||||
'bisa_beli_pupuk_bersubsidi' => ['nullable', 'string', Rule::in(BisaBeliPupukBersubsidi::values())],
|
||||
'manfaat_lain_kartu_tani' => ['nullable', 'string'],
|
||||
@@ -589,6 +601,14 @@ class SosialEkonomiController extends Controller
|
||||
|
||||
$payload = $this->normalizeSkorPayload($payload);
|
||||
|
||||
if (array_key_exists('ada_sengketa', $payload) && !filter_var($payload['ada_sengketa'], FILTER_VALIDATE_BOOLEAN)) {
|
||||
$payload['keterangan_sengketa'] = null;
|
||||
$payload['pihak_membantu_konflik'] = null;
|
||||
$payload['pihak_membantu_konflik_nama'] = null;
|
||||
$payload['tahun_bantuan_konflik'] = null;
|
||||
$payload['status_konflik'] = null;
|
||||
}
|
||||
|
||||
if (($payload['pihak_membantu_konflik'] ?? null) === PihakMembantuKonflik::TIDAK_ADA_SELESAIKAN_SENDIRI->value) {
|
||||
$payload['pihak_membantu_konflik_nama'] = null;
|
||||
$payload['tahun_bantuan_konflik'] = null;
|
||||
@@ -615,6 +635,10 @@ class SosialEkonomiController extends Controller
|
||||
$payload['nama_kelompok_tani'] = null;
|
||||
$payload['lama_bergabung_kelompok_tahun'] = null;
|
||||
$payload['kedekatan_sesama_anggota'] = null;
|
||||
$payload['keaktifan_pertemuan_kelompok'] = KeaktifanPertemuanKelompok::TIDAK_PERNAH_HADIR->value;
|
||||
$payload['topik_biasa_dibahas'] = null;
|
||||
$payload['peran_dalam_kelompok'] = PeranDalamKelompok::ANGGOTA_PASIF->value;
|
||||
$payload['manfaat_kelompok_tani'] = ManfaatKelompokTani::TIDAK_ADA_MANFAAT->value;
|
||||
}
|
||||
|
||||
if (
|
||||
@@ -958,8 +982,11 @@ class SosialEkonomiController extends Controller
|
||||
'terdaftarKelompokTaniTidakValue' => TerdaftarKelompokTani::TIDAK->value,
|
||||
'kedekatanSesamaAnggotaOptions' => KedekatanSesamaAnggota::options(),
|
||||
'keaktifanPertemuanKelompokOptions' => KeaktifanPertemuanKelompok::options(),
|
||||
'keaktifanPertemuanKelompokLowestValue' => KeaktifanPertemuanKelompok::TIDAK_PERNAH_HADIR->value,
|
||||
'peranDalamKelompokOptions' => PeranDalamKelompok::options(),
|
||||
'peranDalamKelompokLowestValue' => PeranDalamKelompok::ANGGOTA_PASIF->value,
|
||||
'manfaatKelompokTaniOptions' => ManfaatKelompokTani::options(),
|
||||
'manfaatKelompokTaniLowestValue' => ManfaatKelompokTani::TIDAK_ADA_MANFAAT->value,
|
||||
'statusKartuTaniOptions' => StatusKartuTani::options(),
|
||||
'bisaBeliPupukBersubsidiOptions' => BisaBeliPupukBersubsidi::options(),
|
||||
'statusRekeningTabunganOptions' => StatusRekeningTabungan::options(),
|
||||
|
||||
@@ -0,0 +1,346 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Web\Produktivitas;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
use App\Models\Pertanian\KelompokTani;
|
||||
use App\Models\Wilayah\Provinsi;
|
||||
use App\Models\Pertanian\Komoditas;
|
||||
|
||||
class SurveyKelompokTaniController extends Controller
|
||||
{
|
||||
private function authorizeAccess(): void
|
||||
{
|
||||
if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) {
|
||||
abort(403);
|
||||
}
|
||||
}
|
||||
|
||||
private function redirectToSection(string $url, string $section, string $message)
|
||||
{
|
||||
return redirect($url . '#section-' . $section)->with([
|
||||
'success' => $message,
|
||||
'success_section' => $section,
|
||||
]);
|
||||
}
|
||||
|
||||
public function index(Request $request)
|
||||
{
|
||||
$kelompokTani = KelompokTani::query()
|
||||
->when($request->search, fn ($query, $search) => $query->where('nama_kelompok', 'like', "%{$search}%"))
|
||||
->latest()
|
||||
->paginate($request->per_page ?? 10)
|
||||
->appends($request->query());
|
||||
|
||||
return view('produktivitas.survey-kelompok-tani.index', compact('kelompokTani'));
|
||||
}
|
||||
|
||||
public function create()
|
||||
{
|
||||
$this->authorizeAccess();
|
||||
|
||||
$provinsi = Provinsi::orderBy('kode', 'asc')->get();
|
||||
$komoditas = Komoditas::where('is_utama', true)->get();
|
||||
|
||||
return view('produktivitas.survey-kelompok-tani.add', compact('provinsi', 'komoditas'));
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$this->authorizeAccess();
|
||||
|
||||
$validated = $this->validateIdentitas($request);
|
||||
|
||||
if (($validated['jenis_kelompok'] ?? null) === 'Lainnya') {
|
||||
$validated['jenis_kelompok'] = $validated['jenis_kelompok_lainnya'];
|
||||
}
|
||||
unset($validated['jenis_kelompok_lainnya']);
|
||||
|
||||
$kelompokTani = KelompokTani::create([
|
||||
...$validated,
|
||||
'uid' => 'KLP-' . strtoupper(Str::random(10)),
|
||||
'created_by' => auth()->id(),
|
||||
'updated_by' => auth()->id(),
|
||||
]);
|
||||
|
||||
return $this->redirectToSection(route('kelompok-tani.edit', $kelompokTani->id), 'identitas', 'Data kelompok tani berhasil ditambahkan.');
|
||||
}
|
||||
|
||||
private function editUrl(KelompokTani $kelompokTani, string $section = 'identitas'): string
|
||||
{
|
||||
$url = route('kelompok-tani.edit', $kelompokTani->id);
|
||||
|
||||
return $section === 'identitas' ? $url : $url . '?section=' . $section;
|
||||
}
|
||||
|
||||
public function edit(Request $request, KelompokTani $kelompokTani)
|
||||
{
|
||||
$this->authorizeAccess();
|
||||
|
||||
return match ($request->query('section', 'identitas')) {
|
||||
'kelembagaan' => view('produktivitas.survey-kelompok-tani.kelembagaan', compact('kelompokTani')),
|
||||
'aktivitas-fungsi' => view('produktivitas.survey-kelompok-tani.aktivitas-fungsi', compact('kelompokTani')),
|
||||
'sosial-konflik' => view('produktivitas.survey-kelompok-tani.sosial-konflik', compact('kelompokTani')),
|
||||
default => view('produktivitas.survey-kelompok-tani.edit', [
|
||||
'kelompokTani' => $kelompokTani,
|
||||
'provinsi' => Provinsi::orderBy('kode', 'asc')->get(),
|
||||
'komoditas' => Komoditas::where('is_utama', true)->get(),
|
||||
]),
|
||||
};
|
||||
}
|
||||
|
||||
public function update(Request $request, KelompokTani $kelompokTani)
|
||||
{
|
||||
$this->authorizeAccess();
|
||||
|
||||
return match ($request->input('section', 'identitas')) {
|
||||
'ringkasan-pengurus' => $this->updateRingkasanPengurus($request, $kelompokTani),
|
||||
'kelembagaan' => $this->updateKelembagaan($request, $kelompokTani),
|
||||
'aktivitas-fungsi' => $this->updateAktivitasFungsi($request, $kelompokTani),
|
||||
'kas' => $this->updateKasSimpanPinjam($request, $kelompokTani),
|
||||
'sosial-konflik' => $this->updateSosialKonflik($request, $kelompokTani),
|
||||
'status-bantuan' => $this->updateStatusBantuan($request, $kelompokTani),
|
||||
'penilaian' => $this->updatePenilaian($request, $kelompokTani),
|
||||
default => $this->updateIdentitas($request, $kelompokTani),
|
||||
};
|
||||
}
|
||||
|
||||
public function destroy(string $id)
|
||||
{
|
||||
$this->authorizeAccess();
|
||||
|
||||
$kelompokTani = KelompokTani::findOrFail($id);
|
||||
$kelompokTani->update(['deleted_by' => auth()->id()]);
|
||||
$kelompokTani->delete();
|
||||
|
||||
return response()->json(['message' => 'Data kelompok tani berhasil dihapus.']);
|
||||
}
|
||||
|
||||
private function validateIdentitas(Request $request)
|
||||
{
|
||||
return $request->validate([
|
||||
// Data Responden
|
||||
'nama_responden' => ['required', 'string', 'max:255'],
|
||||
'jabatan_responden' => ['nullable', 'string', 'max:255'],
|
||||
'kontak_responden' => ['nullable', 'string', 'max:20'],
|
||||
|
||||
// Identitas Kelompok Tani
|
||||
'nama_kelompok' => ['required', 'string', 'max:255'],
|
||||
'desa_kelurahan_id' => ['required', 'exists:master_desa_kelurahan,id'],
|
||||
'dusun' => ['nullable', 'string', 'max:255'],
|
||||
'tahun_berdiri' => ['nullable', 'digits:4', 'integer', 'min:1900', 'max:' . date('Y')],
|
||||
'jenis_kelompok' => ['required', 'string', 'max:50'],
|
||||
'jenis_kelompok_lainnya' => ['required_if:jenis_kelompok,Lainnya', 'nullable', 'string', 'max:255'],
|
||||
'nama_gapoktan' => ['nullable', 'string', 'max:255'],
|
||||
'komoditas_id' => ['nullable', 'array'],
|
||||
'komoditas_id.*' => ['exists:master_komoditas,id'],
|
||||
], [
|
||||
'nama_responden.required' => 'Nama responden wajib diisi.',
|
||||
'nama_kelompok.required' => 'Nama kelompok wajib diisi.',
|
||||
'desa_kelurahan_id.required' => 'Desa/Kelurahan wajib dipilih.',
|
||||
'desa_kelurahan_id.exists' => 'Desa/Kelurahan tidak ditemukan.',
|
||||
'jenis_kelompok.required' => 'Jenis kelompok wajib dipilih.',
|
||||
'jenis_kelompok_lainnya.required_if' => 'Sebutkan jenis kelompok lainnya.',
|
||||
'tahun_berdiri.digits' => 'Tahun berdiri harus 4 digit.',
|
||||
]);
|
||||
}
|
||||
|
||||
private function updateIdentitas(Request $request, KelompokTani $kelompokTani)
|
||||
{
|
||||
$validated = $this->validateIdentitas($request);
|
||||
|
||||
if (($validated['jenis_kelompok'] ?? null) === 'Lainnya') {
|
||||
$validated['jenis_kelompok'] = $validated['jenis_kelompok_lainnya'];
|
||||
}
|
||||
unset($validated['jenis_kelompok_lainnya']);
|
||||
|
||||
$kelompokTani->update([
|
||||
...$validated,
|
||||
'updated_by' => auth()->id(),
|
||||
]);
|
||||
|
||||
return $this->redirectToSection(route('kelompok-tani.edit', $kelompokTani->id), 'identitas', 'Data kelompok tani berhasil diperbarui.');
|
||||
}
|
||||
|
||||
private function updateRingkasanPengurus(Request $request, KelompokTani $kelompokTani)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'jumlah_pengurus' => ['nullable', 'integer', 'min:0'],
|
||||
'jumlah_pengurus_aktif' => ['nullable', 'integer', 'min:0'],
|
||||
'jumlah_anggota_terdaftar' => ['nullable', 'integer', 'min:0'],
|
||||
'jumlah_anggota_aktif' => ['nullable', 'integer', 'min:0'],
|
||||
'jumlah_pengurus_laki_laki' => ['nullable', 'integer', 'min:0'],
|
||||
'jumlah_pengurus_perempuan' => ['nullable', 'integer', 'min:0'],
|
||||
'jumlah_anggota_laki_laki' => ['nullable', 'integer', 'min:0'],
|
||||
'jumlah_anggota_perempuan' => ['nullable', 'integer', 'min:0'],
|
||||
'jumlah_anggota_pemuda_laki_laki' => ['nullable', 'integer', 'min:0'],
|
||||
'jumlah_anggota_pemuda_perempuan' => ['nullable', 'integer', 'min:0'],
|
||||
]);
|
||||
|
||||
$kelompokTani->update([
|
||||
...$validated,
|
||||
'updated_by' => auth()->id(),
|
||||
]);
|
||||
|
||||
return $this->redirectToSection(route('kelompok-tani.pengurus-anggota.index', $kelompokTani->id), 'ringkasan', 'Ringkasan pengurus & anggota berhasil disimpan.');
|
||||
}
|
||||
|
||||
private function updateKelembagaan(Request $request, KelompokTani $kelompokTani)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
// Cakupan Wilayah Kelompok
|
||||
'cakupan_wilayah' => ['nullable', 'string', 'max:50'],
|
||||
'nama_wilayah_cakupan' => ['nullable', 'string'],
|
||||
|
||||
// Status Legalitas Kelompok
|
||||
'status_legalitas' => ['nullable', 'string', 'max:50'],
|
||||
'nomor_sk' => ['nullable', 'string', 'max:100'],
|
||||
'tanggal_tahun_penerbitan_sk' => ['nullable', 'string', 'max:30'],
|
||||
|
||||
// Kualitas Kepemimpinan
|
||||
'tingkat_keaktifan_ketua' => ['nullable', 'string', 'max:30'],
|
||||
'status_pembagian_peran' => ['nullable', 'string', 'max:30'],
|
||||
'status_dokumentasi_rapat' => ['nullable', 'string', 'max:30'],
|
||||
|
||||
// Frekuensi Pertemuan Rutin
|
||||
'frekuensi_pertemuan' => ['nullable', 'string', 'max:50'],
|
||||
'tanggal_pertemuan_terakhir' => ['nullable', 'date'],
|
||||
]);
|
||||
|
||||
$kelompokTani->update([
|
||||
...$validated,
|
||||
'updated_by' => auth()->id(),
|
||||
]);
|
||||
|
||||
return $this->redirectToSection($this->editUrl($kelompokTani, 'kelembagaan'), 'kelembagaan', 'Data kelembagaan berhasil disimpan.');
|
||||
}
|
||||
|
||||
private function resolveLainnya(?array $selected, ?string $lainnyaText): ?array
|
||||
{
|
||||
if (empty($selected)) {
|
||||
return $selected;
|
||||
}
|
||||
|
||||
return array_values(array_map(function ($item) use ($lainnyaText) {
|
||||
return $item === 'Lainnya' && filled($lainnyaText) ? $lainnyaText : $item;
|
||||
}, $selected));
|
||||
}
|
||||
|
||||
private function updateAktivitasFungsi(Request $request, KelompokTani $kelompokTani)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
// Kegiatan Konkret yang Sudah Berjalan
|
||||
'kegiatan_terlaksana' => ['nullable', 'array'],
|
||||
'kegiatan_terlaksana.*' => ['string', 'max:255'],
|
||||
'kegiatan_terlaksana_lainnya' => ['nullable', 'string', 'max:255'],
|
||||
|
||||
'estimasi_panen_minimal' => ['nullable', 'numeric', 'min:0'],
|
||||
'estimasi_panen_maksimal' => ['nullable', 'numeric', 'min:0', 'gte:estimasi_panen_minimal'],
|
||||
'satuan_hasil_panen' => ['nullable', 'string', 'max:20'],
|
||||
|
||||
'tindakan_hasil_panen' => ['nullable', 'array'],
|
||||
'tindakan_hasil_panen.*' => ['string', 'max:255'],
|
||||
|
||||
// Peran dan Fungsi Kelompok
|
||||
'fungsi_kelompok_bagi_anggota' => ['nullable', 'array'],
|
||||
'fungsi_kelompok_bagi_anggota.*' => ['string', 'max:255'],
|
||||
'fungsi_kelompok_lainnya' => ['nullable', 'string', 'max:255'],
|
||||
], [
|
||||
'estimasi_panen_maksimal.gte' => 'Estimasi panen maksimal tidak boleh kurang dari estimasi minimal.',
|
||||
]);
|
||||
|
||||
$validated['kegiatan_terlaksana'] = $this->resolveLainnya(
|
||||
$validated['kegiatan_terlaksana'] ?? null,
|
||||
$validated['kegiatan_terlaksana_lainnya'] ?? null
|
||||
);
|
||||
unset($validated['kegiatan_terlaksana_lainnya']);
|
||||
|
||||
$validated['fungsi_kelompok_bagi_anggota'] = $this->resolveLainnya(
|
||||
$validated['fungsi_kelompok_bagi_anggota'] ?? null,
|
||||
$validated['fungsi_kelompok_lainnya'] ?? null
|
||||
);
|
||||
unset($validated['fungsi_kelompok_lainnya']);
|
||||
|
||||
$kelompokTani->update([
|
||||
...$validated,
|
||||
'updated_by' => auth()->id(),
|
||||
]);
|
||||
|
||||
return $this->redirectToSection($this->editUrl($kelompokTani, 'aktivitas-fungsi'), 'aktivitas-fungsi', 'Data aktivitas & fungsi kelompok berhasil disimpan.');
|
||||
}
|
||||
|
||||
private function updateKasSimpanPinjam(Request $request, KelompokTani $kelompokTani)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'pembukuan_kas' => ['nullable', 'string', 'max:50'],
|
||||
'pengelola_kas' => ['nullable', 'string', 'max:255'],
|
||||
'masalah_pengelolaan_kas_simpan_pinjam' => ['nullable', 'string', 'max:255'],
|
||||
'deskripsi_masalah_pengelolaan_kas_simpan_pinjam' => ['nullable', 'string'],
|
||||
]);
|
||||
|
||||
$kelompokTani->update([
|
||||
...$validated,
|
||||
'updated_by' => auth()->id(),
|
||||
]);
|
||||
|
||||
return $this->redirectToSection(route('kelompok-tani.aset-keuangan.index', $kelompokTani->id), 'kas', 'Data kas/simpan-pinjam berhasil disimpan.');
|
||||
}
|
||||
|
||||
private function updateSosialKonflik(Request $request, KelompokTani $kelompokTani)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
// Keterlibatan Perempuan
|
||||
'partisipasi_perempuan' => ['nullable', 'string', 'max:255'],
|
||||
|
||||
// Riwayat Konflik Internal Kelompok
|
||||
'konflik_kelompok' => ['nullable', 'string', 'max:255'],
|
||||
'topik_konflik' => ['nullable', 'string', 'max:255'],
|
||||
'penyelesaian_konflik' => ['nullable', 'string'],
|
||||
]);
|
||||
|
||||
if (!in_array($validated['konflik_kelompok'] ?? null, ['Pernah, sudah selesai', 'Pernah, masih berlanjut/belum selesai'])) {
|
||||
$validated['topik_konflik'] = null;
|
||||
$validated['penyelesaian_konflik'] = null;
|
||||
}
|
||||
|
||||
$kelompokTani->update([
|
||||
...$validated,
|
||||
'updated_by' => auth()->id(),
|
||||
]);
|
||||
|
||||
return $this->redirectToSection($this->editUrl($kelompokTani, 'sosial-konflik'), 'sosial-konflik', 'Data sosial & konflik berhasil disimpan.');
|
||||
}
|
||||
|
||||
private function updateStatusBantuan(Request $request, KelompokTani $kelompokTani)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'pernah_mendapatkan_bantuan' => ['required', 'boolean'],
|
||||
]);
|
||||
|
||||
$kelompokTani->update([
|
||||
...$validated,
|
||||
'updated_by' => auth()->id(),
|
||||
]);
|
||||
|
||||
return $this->redirectToSection(route('kelompok-tani.bantuan-penilaian.index', $kelompokTani->id), 'status-bantuan', 'Status akses bantuan berhasil disimpan.');
|
||||
}
|
||||
|
||||
private function updatePenilaian(Request $request, KelompokTani $kelompokTani)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'potensi_kelompok' => ['nullable', 'string', 'max:50'],
|
||||
'alasan_penilaian_potensi' => ['nullable', 'string'],
|
||||
]);
|
||||
|
||||
$kelompokTani->update([
|
||||
...$validated,
|
||||
'updated_by' => auth()->id(),
|
||||
]);
|
||||
|
||||
return $this->redirectToSection(route('kelompok-tani.bantuan-penilaian.index', $kelompokTani->id), 'penilaian', 'Penilaian potensi kelompok berhasil disimpan.');
|
||||
}
|
||||
}
|
||||
@@ -3,26 +3,43 @@
|
||||
namespace App\Http\Controllers\Web\Produktivitas;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
use App\Models\Pertanian\Petani;
|
||||
use App\Models\Pertanian\SurveyProduktivitas;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class SurveyProduktivitasController extends Controller
|
||||
{
|
||||
public function store($petaniId, Request $request)
|
||||
{
|
||||
$petani = Petani::findOrFail($petaniId);
|
||||
$validated = $request->validate([
|
||||
'produktivitas_komoditas' => ['required', 'array'],
|
||||
'produktivitas_komoditas.*.komoditas_id' => ['nullable', 'exists:master_komoditas,id'],
|
||||
'produktivitas_komoditas.*.hasil_panen_3_musim' => ['nullable', 'numeric'],
|
||||
'produktivitas_komoditas.*.satuan' => ['nullable', 'string'],
|
||||
|
||||
$validator = Validator::make($request->all(), [
|
||||
'produktivitas_komoditas' => ['required', 'array', 'min:1'],
|
||||
'produktivitas_komoditas.*.komoditas_id' => ['required', 'exists:master_komoditas,id'],
|
||||
'produktivitas_komoditas.*.hasil_panen_3_musim' => ['required', 'numeric', 'min:0'],
|
||||
'produktivitas_komoditas.*.satuan' => ['required', 'in:ha_tahun,pohon_tahun'],
|
||||
], [
|
||||
'produktivitas_komoditas.required' => 'Minimal satu data produktivitas harus diisi.',
|
||||
'produktivitas_komoditas.*.komoditas_id.required' => 'Komoditas wajib dipilih.',
|
||||
'produktivitas_komoditas.*.komoditas_id.exists' => 'Komoditas yang dipilih tidak valid.',
|
||||
'produktivitas_komoditas.*.hasil_panen_3_musim.required' => 'Hasil panen wajib diisi.',
|
||||
'produktivitas_komoditas.*.hasil_panen_3_musim.numeric' => 'Hasil panen harus berupa angka.',
|
||||
'produktivitas_komoditas.*.hasil_panen_3_musim.min' => 'Hasil panen tidak boleh kurang dari 0.',
|
||||
'produktivitas_komoditas.*.satuan.required' => 'Satuan wajib dipilih.',
|
||||
'produktivitas_komoditas.*.satuan.in' => 'Satuan yang dipilih tidak valid.',
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return $this->redirectBackToFragment($request)->withErrors($validator)->withInput();
|
||||
}
|
||||
|
||||
$validated = $validator->validated();
|
||||
|
||||
foreach ($validated['produktivitas_komoditas'] as $row) {
|
||||
SurveyProduktivitas::create([
|
||||
'uid' => 'SPV-' . strtoupper(Str::random(10)),
|
||||
'uid' => 'SPV-'.strtoupper(Str::random(10)),
|
||||
'petani_id' => $petani->id,
|
||||
'komoditas_id' => $row['komoditas_id'],
|
||||
'kuantitas' => $row['hasil_panen_3_musim'],
|
||||
@@ -32,10 +49,10 @@ class SurveyProduktivitasController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
return back()->with('success', 'Data produktivitas berhasil disimpan.');
|
||||
return $this->redirectBackToFragment($request)->with('success', 'Data produktivitas berhasil disimpan.');
|
||||
}
|
||||
|
||||
public function destroy($petaniId, $surveyProduktivitasId)
|
||||
public function destroy($petaniId, $surveyProduktivitasId, Request $request)
|
||||
{
|
||||
$surveyProduktivitas = SurveyProduktivitas::where('petani_id', $petaniId)
|
||||
->where('id', $surveyProduktivitasId)
|
||||
@@ -44,6 +61,17 @@ class SurveyProduktivitasController extends Controller
|
||||
$surveyProduktivitas->save();
|
||||
$surveyProduktivitas->delete();
|
||||
|
||||
return back()->with('success', 'Data produktivitas berhasil dihapus.');
|
||||
return $this->redirectBackToFragment($request)->with('success', 'Data produktivitas berhasil dihapus.');
|
||||
}
|
||||
|
||||
private function redirectBackToFragment(Request $request)
|
||||
{
|
||||
$fragment = $request->input('redirect_fragment');
|
||||
|
||||
if (! is_string($fragment) || ! preg_match('/^[A-Za-z0-9_-]+$/', $fragment)) {
|
||||
return back();
|
||||
}
|
||||
|
||||
return redirect()->to(Str::before(url()->previous(), '#').'#'.$fragment);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Bumdes;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class KelembagaanAktorBumdes extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
protected $table = 'bumdes_kelembagaan_aktor';
|
||||
|
||||
protected $fillable = [
|
||||
'pengurus_bumdes_id',
|
||||
'status_badan_hukum',
|
||||
'nomor_dasar_hukum',
|
||||
'tahun_dasar_hukum',
|
||||
'status_keaktifan',
|
||||
'alasan_tidak_aktif',
|
||||
'unit_usaha',
|
||||
'unit_usaha_jasa_lainnya',
|
||||
'status_unit_komoditas',
|
||||
'nama_unit_komoditas',
|
||||
'status_ad_art',
|
||||
'tahun_ad_art',
|
||||
'pengurus_inti',
|
||||
'memiliki_staf',
|
||||
'jumlah_staf',
|
||||
'unit_usaha_staf',
|
||||
'modal_awal',
|
||||
'tahun_modal_awal',
|
||||
'pernah_penambahan_modal',
|
||||
'penambahan_modal',
|
||||
'aset_produktif',
|
||||
'aset_produktif_lainnya',
|
||||
'status_kepemilikan_aset',
|
||||
'infrastruktur_pascapanen',
|
||||
'infrastruktur_pascapanen_lainnya',
|
||||
'created_by',
|
||||
'updated_by',
|
||||
'deleted_by',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'tahun_dasar_hukum' => 'integer',
|
||||
'tahun_ad_art' => 'integer',
|
||||
'unit_usaha' => 'array',
|
||||
'pengurus_inti' => 'array',
|
||||
'memiliki_staf' => 'boolean',
|
||||
'jumlah_staf' => 'integer',
|
||||
'modal_awal' => 'decimal:2',
|
||||
'tahun_modal_awal' => 'integer',
|
||||
'pernah_penambahan_modal' => 'boolean',
|
||||
'penambahan_modal' => 'array',
|
||||
'aset_produktif' => 'array',
|
||||
'infrastruktur_pascapanen' => 'array',
|
||||
];
|
||||
|
||||
public function pengurusBumdes(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(PengurusBumdes::class, 'pengurus_bumdes_id');
|
||||
}
|
||||
|
||||
public function creator(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Bumdes;
|
||||
|
||||
use App\Enums\Bumdes\JabatanBumdes;
|
||||
use App\Enums\Pendidikan;
|
||||
use App\Models\User;
|
||||
use App\Models\Wilayah\DesaKelurahan;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class PengurusBumdes extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
protected $table = 'bumdes_pengurus';
|
||||
|
||||
protected $fillable = [
|
||||
'uid',
|
||||
'nama',
|
||||
'jenis_kelamin',
|
||||
'tempat_lahir',
|
||||
'tanggal_lahir',
|
||||
'pendidikan_terakhir',
|
||||
'alamat',
|
||||
'desa_kelurahan_id',
|
||||
'nomor_telepon',
|
||||
'jabatan',
|
||||
'jabatan_lainnya',
|
||||
'lama_menjabat_tahun',
|
||||
'tanggal_mulai_menjabat',
|
||||
'created_by',
|
||||
'updated_by',
|
||||
'deleted_by',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'jenis_kelamin' => 'boolean',
|
||||
'tanggal_lahir' => 'date',
|
||||
'pendidikan_terakhir' => Pendidikan::class,
|
||||
'tanggal_mulai_menjabat' => 'date',
|
||||
'jabatan' => JabatanBumdes::class,
|
||||
];
|
||||
|
||||
public function getUsiaAttribute(): ?int
|
||||
{
|
||||
if (! $this->tanggal_lahir) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Carbon::parse($this->tanggal_lahir)->age;
|
||||
}
|
||||
|
||||
public function getLamaMenjabatAttribute(): string
|
||||
{
|
||||
if (! is_null($this->lama_menjabat_tahun)) {
|
||||
return $this->lama_menjabat_tahun . ' tahun';
|
||||
}
|
||||
|
||||
if (! $this->tanggal_mulai_menjabat) {
|
||||
return '-';
|
||||
}
|
||||
|
||||
$tanggalMulai = Carbon::parse($this->tanggal_mulai_menjabat)->startOfDay();
|
||||
|
||||
if ($tanggalMulai->isFuture()) {
|
||||
return 'Belum mulai';
|
||||
}
|
||||
|
||||
$selisih = $tanggalMulai->diff(Carbon::now());
|
||||
$bagian = [];
|
||||
|
||||
if ($selisih->y > 0) {
|
||||
$bagian[] = $selisih->y . ' tahun';
|
||||
}
|
||||
|
||||
if ($selisih->m > 0) {
|
||||
$bagian[] = $selisih->m . ' bulan';
|
||||
}
|
||||
|
||||
if (empty($bagian)) {
|
||||
return $selisih->d . ' hari';
|
||||
}
|
||||
|
||||
return implode(' ', $bagian);
|
||||
}
|
||||
|
||||
public function getNamaJabatanAttribute(): string
|
||||
{
|
||||
if ($this->jabatan === JabatanBumdes::LAINNYA && $this->jabatan_lainnya) {
|
||||
return $this->jabatan_lainnya;
|
||||
}
|
||||
|
||||
return $this->jabatan?->label() ?? '-';
|
||||
}
|
||||
|
||||
public function desaKelurahan(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(DesaKelurahan::class, 'desa_kelurahan_id');
|
||||
}
|
||||
|
||||
public function creator(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by');
|
||||
}
|
||||
|
||||
public function kelembagaanAktor(): HasOne
|
||||
{
|
||||
return $this->hasOne(KelembagaanAktorBumdes::class, 'pengurus_bumdes_id');
|
||||
}
|
||||
}
|
||||
@@ -56,7 +56,6 @@ class FormBasisData extends Model
|
||||
public function form(): HasMany
|
||||
{
|
||||
return $this->hasMany(FormBasisData::class, 'parent_id')
|
||||
->with('form')
|
||||
->orderBy('urutan');
|
||||
}
|
||||
|
||||
|
||||
@@ -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'
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Master;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
||||
class JenisTanaman extends Model
|
||||
{
|
||||
use HasFactory, SoftDeletes;
|
||||
|
||||
protected $table = 'master_jenis_tanaman';
|
||||
|
||||
protected $fillable = [
|
||||
'nama',
|
||||
'created_by',
|
||||
'updated_by',
|
||||
'deleted_by',
|
||||
];
|
||||
}
|
||||
@@ -22,15 +22,25 @@ class AksesPasar extends Model
|
||||
protected $fillable = [
|
||||
'uid',
|
||||
'petani_id',
|
||||
'tujuan_penjualan',
|
||||
'tujuan_penjualan_lainnya',
|
||||
'sifat_hubungan_bisnis',
|
||||
'bentuk_penjualan',
|
||||
'frekuensi_penjualan',
|
||||
'skor_pengalaman',
|
||||
'saluran_terdahulu',
|
||||
'saluran_terdahulu_lainnya',
|
||||
'alasan_berhenti',
|
||||
'tahu_harga_sebelumnya',
|
||||
'sumber_info_harga',
|
||||
'sumber_info_harga_lainnya',
|
||||
'praktik_fermentasi',
|
||||
'proses_fermentasi',
|
||||
'model_fermentasi_memungkinkan',
|
||||
'skor_kesadaran_premium',
|
||||
'sumber_info_premium',
|
||||
'tingkat_kepercayaan_pembeli_baru',
|
||||
'alasan_kepercayaan_pembeli_baru',
|
||||
'waktu_mulai_wawancara',
|
||||
'waktu_selesai_wawancara',
|
||||
'skor_kepercayaan_responden',
|
||||
@@ -42,7 +52,9 @@ class AksesPasar extends Model
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'tujuan_penjualan' => 'array',
|
||||
'saluran_terdahulu' => 'array',
|
||||
'sumber_info_harga' => 'array',
|
||||
];
|
||||
|
||||
protected static function booted(): void
|
||||
@@ -50,7 +62,7 @@ class AksesPasar extends Model
|
||||
static::saved(function (AksesPasar $aksesPasar) {
|
||||
|
||||
$petani = Petani::find($aksesPasar->petani_id);
|
||||
$skor = SkorPetani::where('petani_id')->orderBy('id')->first();
|
||||
$skor = SkorPetani::where('petani_id', $petani->id)->orderBy('id')->first();
|
||||
if (!$skor) {
|
||||
$petaniUid = $petani->uid;
|
||||
$part = explode('-', $petaniUid);
|
||||
@@ -61,6 +73,7 @@ class AksesPasar extends Model
|
||||
'updated_by' => $petani->updated_by
|
||||
]);
|
||||
}
|
||||
|
||||
$skor->update([
|
||||
'skor_d6_k1_6_pengalaman_menjual_ke_saluran_berbeda' => $aksesPasar->skor_pengalaman,
|
||||
'skor_d6_k4_1_kesadaran_akses_pasar_premium' => $aksesPasar->skor_kesadaran_premium,
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
<?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;
|
||||
|
||||
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');
|
||||
}
|
||||
}
|
||||
@@ -16,11 +16,17 @@ class Komoditas extends Model
|
||||
|
||||
protected $table = 'master_komoditas';
|
||||
|
||||
protected $casts = [
|
||||
'is_utama' => 'boolean',
|
||||
];
|
||||
|
||||
protected $fillable = [
|
||||
'nama',
|
||||
'kode',
|
||||
'created_by',
|
||||
'updated_by',
|
||||
'deleted_by',
|
||||
'is_utama',
|
||||
];
|
||||
|
||||
protected static function booted()
|
||||
|
||||
@@ -18,6 +18,7 @@ use App\Enums\KondisiTanaman;
|
||||
use App\Enums\SeranganHama;
|
||||
use App\Enums\PenyakitTanaman;
|
||||
use App\Enums\KeparahanSerangan;
|
||||
use App\Models\Pertanian\Petani;
|
||||
|
||||
class Lahan extends Model
|
||||
{
|
||||
@@ -56,7 +57,10 @@ class Lahan extends Model
|
||||
'komoditas_lain',
|
||||
'tanaman_penaung',
|
||||
|
||||
'foto'
|
||||
'foto',
|
||||
|
||||
'jenis_hama',
|
||||
'jenis_penyakit',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
@@ -68,7 +72,9 @@ class Lahan extends Model
|
||||
'serangan_hama' => SeranganHama::class,
|
||||
'penyakit_tanaman' => PenyakitTanaman::class,
|
||||
'keparahan_serangan' => KeparahanSerangan::class,
|
||||
'foto' => 'array'
|
||||
'foto' => 'array',
|
||||
'jenis_hama' => 'array',
|
||||
'jenis_penyakit' => 'array'
|
||||
];
|
||||
|
||||
public function profile(): BelongsTo
|
||||
@@ -93,7 +99,7 @@ class Lahan extends Model
|
||||
|
||||
public function petani(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Profile::class, 'petani_id');
|
||||
return $this->belongsTo(Petani::class, 'petani_id');
|
||||
}
|
||||
|
||||
public function komoditas(): BelongsTo
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
@@ -94,15 +94,15 @@ class Petani extends Model
|
||||
protected static function booted(): void
|
||||
{
|
||||
static::saved(function (Petani $petani) {
|
||||
|
||||
$skor = SkorPetani::where('petani_id')->orderBy('id')->first();
|
||||
if (! $skor) {
|
||||
$skor = SkorPetani::where('petani_id', $petani->id)->orderBy('id')->first();
|
||||
if (!$skor) {
|
||||
$petaniUid = $petani->uid;
|
||||
$part = explode('-', $petaniUid);
|
||||
$skor = SkorPetani::create([
|
||||
'uid' => 'SKR-'.$part[1].'-'.strtoupper(Str::random(10)),
|
||||
'petani_id' => $petani->id,
|
||||
'uid' => 'SKR-' . $part[1] . '-' . strtoupper(Str::random(10)),
|
||||
'created_by' => $petani->created_by,
|
||||
'updated_by' => $petani->updated_by,
|
||||
'updated_by' => $petani->updated_by
|
||||
]);
|
||||
}
|
||||
});
|
||||
@@ -138,9 +138,9 @@ class Petani extends Model
|
||||
return $this->hasMany(SurveySertifikasi::class, 'petani_id');
|
||||
}
|
||||
|
||||
public function surveyProduktivitas(): HasOne
|
||||
public function surveyProduktivitas(): HasMany
|
||||
{
|
||||
return $this->hasOne(SurveyProduktivitas::class, 'petani_id')->latestOfMany();
|
||||
return $this->hasMany(SurveyProduktivitas::class, 'petani_id');
|
||||
}
|
||||
|
||||
public function lahans(): HasMany
|
||||
@@ -339,7 +339,7 @@ class Petani extends Model
|
||||
$skor >= 3.0 => 'Perintis',
|
||||
$skor >= 2.0 => 'Pemerhati',
|
||||
$skor >= 1.0 => 'Pemula',
|
||||
default => 'Belum tersegmentasi',
|
||||
default => 'Belum lengkap',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -365,10 +365,10 @@ class Petani extends Model
|
||||
} elseif ($skor->skor_kesiapan_adopsi >= 1.0) {
|
||||
return 'Pemula';
|
||||
} else {
|
||||
return 'Belum tersegmentasi';
|
||||
return 'Belum lengkap';
|
||||
}
|
||||
|
||||
return 'Belum tersegmentasi';
|
||||
return 'Belum lengkap';
|
||||
}
|
||||
|
||||
public function keteranganSegmen(): ?string
|
||||
|
||||
@@ -8,10 +8,13 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
use App\Models\Pertanian\Petani;
|
||||
use App\Models\Pertanian\SkorPetani;
|
||||
use App\Models\Pertanian\Tanaman;
|
||||
use App\Models\Pertanian\Lahan;
|
||||
|
||||
class PraktikBudidaya extends Model
|
||||
{
|
||||
@@ -91,16 +94,54 @@ class PraktikBudidaya extends Model
|
||||
'updated_by' => $petani->updated_by
|
||||
]);
|
||||
}
|
||||
$skor->update([
|
||||
|
||||
'skor_d2_k2_3_praktik_pemangkasan' => $praktikBudidaya->frekuensi_pemangkasan_komoditas_utama,
|
||||
$kebiasaan = $praktikBudidaya->kebiasaan_konservasi_tanah ? count($praktikBudidaya->kebiasaan_konservasi_tanah) : 0;
|
||||
$pemahaman = $praktikBudidaya->pemahaman_manfaat_konservasi_tanah == 'Ya, tahu manfaatnya' ? 1 : 0;
|
||||
$skor_konservasi_tanah = 0;
|
||||
$jenisHama = $praktikBudidaya->jenis_hama ?? [];
|
||||
$jenisPenyakit = $praktikBudidaya->jenis_penyakit ?? [];
|
||||
|
||||
if (Schema::hasColumn((new Lahan)->getTable(), 'jenis_hama')
|
||||
&& Schema::hasColumn((new Lahan)->getTable(), 'jenis_penyakit')) {
|
||||
Lahan::where('petani_id', $praktikBudidaya->petani_id)->get()->each(function (Lahan $lahan) use ($jenisHama, $jenisPenyakit, $praktikBudidaya) {
|
||||
$lahan->update([
|
||||
'jenis_hama' => $jenisHama,
|
||||
'jenis_penyakit' => $jenisPenyakit,
|
||||
'updated_by' => $praktikBudidaya->updated_by,
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
if (Schema::hasColumn((new Tanaman)->getTable(), 'jenis_hama')
|
||||
&& Schema::hasColumn((new Tanaman)->getTable(), 'jenis_penyakit')) {
|
||||
Tanaman::where('petani_id', $praktikBudidaya->petani_id)->get()->each(function (Tanaman $tanaman) use ($jenisHama, $jenisPenyakit, $praktikBudidaya) {
|
||||
$tanaman->update([
|
||||
'jenis_hama' => $jenisHama,
|
||||
'jenis_penyakit' => $jenisPenyakit,
|
||||
'updated_by' => $praktikBudidaya->updated_by,
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
if ($kebiasaan == 0) {
|
||||
$skor_konservasi_tanah = 1;
|
||||
} elseif ($kebiasaan == 1 && $pemahaman == 0) {
|
||||
$skor_konservasi_tanah = 2;
|
||||
} elseif ($kebiasaan == 1 && $pemahaman == 1) {
|
||||
$skor_konservasi_tanah = 3;
|
||||
} elseif ($kebiasaan > 1 && $pemahaman == 0) {
|
||||
$skor_konservasi_tanah = 4;
|
||||
} elseif ($kebiasaan > 1 && $pemahaman == 1) {
|
||||
$skor_konservasi_tanah = 5;
|
||||
}
|
||||
$skor->update([
|
||||
'skor_d2_k2_3_praktik_pemangkasan' => $praktikBudidaya->frekuensi_pemangkasan_utama,
|
||||
'skor_d2_k2_4_praktik_pemupukan' => $praktikBudidaya->pemberian_pupuk,
|
||||
'skor_d2_k2_5_pengendalian_opt' => $praktikBudidaya->metode_penanganan_hama_penyakit,
|
||||
'skor_d2_k2_5_pengendalian_opt' => $praktikBudidaya->metode_penanganan_opt,
|
||||
'skor_d2_k2_6_sanitasi_kebun' => $praktikBudidaya->frekuensi_sanitasi_lahan,
|
||||
'skor_d2_k2_6b_pengelolaan_gulma' => $praktikBudidaya->penanganan_gulma,
|
||||
'skor_d2_k2_7_penglolaan_air_irigasi' => $praktikBudidaya->kondisi_pengelolaan_air_irigasi,
|
||||
//'skor_d2_k2_8_praktik_konservasi_tanah' => $praktikBudidaya->kebiasaan_konservasi_tanah,
|
||||
//'skor_d2_k3_1_penentuan_pematangan' => $praktikBudidaya->
|
||||
'skor_d2_k2_7_penglolaan_air_irigasi' => $praktikBudidaya->kondisi_pengelolaan_air,
|
||||
'skor_d2_k2_8_praktik_konservasi_tanah' => $skor_konservasi_tanah,
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,6 +4,9 @@ namespace App\Models\Pertanian;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
use App\Models\Pertanian\Petani;
|
||||
|
||||
class SkorPetani extends Model
|
||||
{
|
||||
@@ -50,13 +53,14 @@ class SkorPetani extends Model
|
||||
'skor_d4_k5_2_pengalaman_kredit_formal',
|
||||
'skor_d4_k5_3_kepemilikan_aset_produktif',
|
||||
'skor_d4_k5_4_kepemilikan_aset_tidak_produktif',
|
||||
'skor_d4_k5_4_5_akses_program_bantuan',
|
||||
'skor_d4_k5_4_akses_program_bantuan',
|
||||
'skor_d4_k6_3_keberadaan_hutang_terkait_panen',
|
||||
'skor_d4_k6_5a_praktik_ijon',
|
||||
'skor_d4_k6_5b_kebebasan_memilih_pembeli',
|
||||
'skor_d6_k1_6_pengalaman_menjual_ke_saluran_berbeda',
|
||||
'skor_d6_k4_1_kesadaran_akses_pasar_premium',
|
||||
'skor_d6_k5_1_hambatan_mutu',
|
||||
'skor_d6_k5_2_tingkat_kepercayaan_pembeli',
|
||||
|
||||
'created_by',
|
||||
'updated_by',
|
||||
@@ -102,7 +106,7 @@ class SkorPetani extends Model
|
||||
$skorPetani->skor_d4_k5_1_kepemilikan_rekening +
|
||||
$skorPetani->skor_d4_k5_2_pengalaman_kredit_formal +
|
||||
$skorPetani->skor_d4_k5_3_kepemilikan_aset_produktif +
|
||||
$skorPetani->skor_d4_k5_4_5_akses_program_bantuan
|
||||
$skorPetani->skor_d4_k5_4_akses_program_bantuan
|
||||
) / 4 +
|
||||
(
|
||||
$skorPetani->skor_d4_k6_3_keberadaan_hutang_terkait_panen +
|
||||
@@ -113,30 +117,36 @@ class SkorPetani extends Model
|
||||
$skorPetani->skor_komp_6_d6_k1_k4_k5_rantai_nilai_akses_pasar = (
|
||||
$skorPetani->skor_d6_k1_6_pengalaman_menjual_ke_saluran_berbeda +
|
||||
$skorPetani->skor_d6_k4_1_kesadaran_akses_pasar_premium +
|
||||
$skorPetani->skor_d6_k5_1_hambatan_mutu
|
||||
) / 3;
|
||||
$skorPetani->skor_d6_k5_1_hambatan_mutu +
|
||||
$skorPetani->skor_d6_k5_2_tingkat_kepercayaan_pembeli
|
||||
) / 4;
|
||||
|
||||
$skorPetani->skor_d2_sistem_praktik_budidaya = (
|
||||
$skorPetani->skor_komp_3_d2_praktik_budidaya_gap
|
||||
);
|
||||
$skorPetani->skor_d4_sosial_ekonomi = (
|
||||
$skorPetani->skor_komp_1_d4_k3_motivasi_kapasitas_adopsi
|
||||
);
|
||||
$skorPetani->skor_d6_rantai_nilai_akses_pasar = (
|
||||
$skorPetani->skor_komp_6_d6_k1_k4_k5_rantai_nilai_akses_pasar * 0.25 +
|
||||
$skorPetani->skor_komp_6_d6_k1_k4_k5_rantai_nilai_akses_pasar
|
||||
);
|
||||
$skorPetani->skor_d4_sosial_ekonomi = (
|
||||
$skorPetani->skor_komp_1_d4_k3_motivasi_kapasitas_adopsi * 0.25 +
|
||||
$skorPetani->skor_komp_2_d4_k2_ekonomi_rumah_tangga * 0.20 +
|
||||
$skorPetani->skor_komp_4_d4_k4_keterlibatan_kelembagaan * 0.15 +
|
||||
$skorPetani->skor_komp_5_d4_k5_k6_akses_kebebasan_keuangan * 0.10
|
||||
) / 0.70;
|
||||
|
||||
$skorPetani->skor_kesiapan_adopsi = (
|
||||
$skorPetani->skor_komp_6_d6_k1_k4_k5_rantai_nilai_akses_pasar * 0.25 +
|
||||
$skorPetani->skor_komp_2_d4_k2_ekonomi_rumah_tangga * 0.20 +
|
||||
$skorPetani->skor_komp_3_d2_praktik_budidaya_gap * 0.20 +
|
||||
$skorPetani->skor_komp_4_d4_k4_keterlibatan_kelembagaan * 0.15 +
|
||||
$skorPetani->skor_komp_5_d4_k5_k6_akses_kebebasan_keuangan * 0.10 +
|
||||
$skorPetani->skor_komp_1_d4_k3_motivasi_kapasitas_adopsi * 0.10
|
||||
$skorPetani->skor_komp_1_d4_k3_motivasi_kapasitas_adopsi * 0.25 + // D4
|
||||
$skorPetani->skor_komp_2_d4_k2_ekonomi_rumah_tangga * 0.20 + // D4
|
||||
$skorPetani->skor_komp_3_d2_praktik_budidaya_gap * 0.20 + // D2
|
||||
$skorPetani->skor_komp_4_d4_k4_keterlibatan_kelembagaan * 0.15 + // D4
|
||||
$skorPetani->skor_komp_5_d4_k5_k6_akses_kebebasan_keuangan * 0.10 + // D4
|
||||
$skorPetani->skor_komp_6_d6_k1_k4_k5_rantai_nilai_akses_pasar * 0.10 // D6
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
public function petani(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Petani::class, 'petani_id');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ use App\Enums\SosialEkonomi\UsahaMutuTambahanHarga;
|
||||
|
||||
use App\Models\Pertanian\Petani;
|
||||
use App\Models\Pertanian\SkorPetani;
|
||||
use App\Models\Pertanian\PraktikBudidaya;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
@@ -204,7 +205,42 @@ class SosialEkonomi extends Model
|
||||
'updated_by' => $petani->updated_by
|
||||
]);
|
||||
}
|
||||
$praktik = PraktikBudidaya::where('petani_id', $petani->id)->orderBy('id')->first();
|
||||
$pendapatan = PendapatanKomoditas::where('sosial_ekonomi_id', $sosialEkonomi->id)->get();
|
||||
|
||||
$rasio = 0;
|
||||
if ($praktik && ($pendapatan && $pendapatan->count() > 0 && $pendapatan->sum('pendapatan') > 0)) {
|
||||
$rasio = $praktik->total_estimasi_biaya_produksi / $pendapatan->sum('pendapatan');
|
||||
}
|
||||
if ($rasio >= 0.8) {
|
||||
$skorRasio = 1;
|
||||
} else if ($rasio >= 0.6) {
|
||||
$skorRasio = 2;
|
||||
} else if ($rasio >= 0.45) {
|
||||
$skorRasio = 3;
|
||||
} else if ($rasio >= 0.3) {
|
||||
$skorRasio = 4;
|
||||
} else {
|
||||
$skorRasio = 5;
|
||||
}
|
||||
|
||||
$skor->update([
|
||||
'skor_d4_k3_1_praktik_baru' => $sosialEkonomi->kemauan_mencoba_praktik_baru?->value,
|
||||
'skor_d4_k3_4_pemahaman_mutu_harga' => $sosialEkonomi->pemahaman_mutu_harga?->value,
|
||||
'skor_d4_k3_5_role_model' => $sosialEkonomi->panutan_petani_sukses?->value,
|
||||
'skor_d4_k3_6_kemampuan_keterlibatan_program' => $sosialEkonomi->kemauan_ikut_program?->value,
|
||||
'skor_d4_k3_7_orientasi_jangka_panjang' => $sosialEkonomi->orientasi_rencana_jangka_panjang?->value,
|
||||
|
||||
'skor_d4_k2_5_stabilitas_penghasilan' => $sosialEkonomi->skor_periode_paceklik?->value,
|
||||
'skor_d4_k2_8_kemampuan_menabung' => $sosialEkonomi->skor_kemampuan_menabung?->value,
|
||||
'skor_d4_k2_9_kecukupan_penghasilan' => $sosialEkonomi->skor_kecukupan_penghasilan?->value,
|
||||
'skor_d4_k2_3_k2_7_rasio_biaya_produksi_pendapatan' => $skorRasio,
|
||||
'skor_d4_k2_11_hak_kelola_lahan' => $sosialEkonomi->skor_kejelasan_status_lahan?->value,
|
||||
|
||||
'skor_d4_k4_2_keaktifan_pertemuan' => $sosialEkonomi->keaktifan_pertemuan_kelompok?->value,
|
||||
'skor_d4_k4_3_peran_dalam_kelompok' => $sosialEkonomi->peran_dalam_kelompok?->value,
|
||||
'skor_d4_k4_4_manfaat_dirasakan' => $sosialEkonomi->manfaat_kelompok_tani?->value,
|
||||
|
||||
'skor_d4_k5_1_kepemilikan_rekening' => $sosialEkonomi->status_rekening_tabungan?->value,
|
||||
'skor_d4_k5_2_pengalaman_kredit_formal' => $sosialEkonomi->pengalaman_kredit_kebun?->value,
|
||||
'skor_d4_k5_3_kepemilikan_aset_produktif' => $sosialEkonomi->kondisi_aset_produktif?->value,
|
||||
|
||||
@@ -44,6 +44,30 @@ class SurveyPanen extends Model
|
||||
'deleted_by',
|
||||
];
|
||||
|
||||
protected static function booted(): void
|
||||
{
|
||||
static::saved(function (SurveyPanen $survey) {
|
||||
|
||||
$petani = Petani::find($survey->petani_id);
|
||||
$skor = SkorPetani::where('petani_id', $petani->id)->orderBy('id')->first();
|
||||
if (!$skor) {
|
||||
$petaniUid = $petani->uid;
|
||||
$part = explode('-', $petaniUid);
|
||||
$skor = SkorPetani::create([
|
||||
'petani_id' => $petani->id,
|
||||
'uid' => 'SKR-' . $part[1] . '-' . strtoupper(Str::random(10)),
|
||||
'created_by' => $petani->created_by,
|
||||
'updated_by' => $petani->updated_by
|
||||
]);
|
||||
}
|
||||
|
||||
$skor->update([
|
||||
'skor_d2_k3_1_penentuan_pematangan' => $survey->dasar_penentuan_panen,
|
||||
'skor_d2_k3_5_sortasi_awal_pemisahan_kualitas' => $survey->praktik_sortasi,
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
public function petani(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Petani::class, 'petani_id');
|
||||
|
||||
@@ -42,6 +42,9 @@ class Tanaman extends Model
|
||||
'serangan_hama',
|
||||
'penyakit_tanaman',
|
||||
'keparahan_serangan',
|
||||
|
||||
'jenis_hama',
|
||||
'jenis_penyakit',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
@@ -49,6 +52,8 @@ class Tanaman extends Model
|
||||
'serangan_hama' => SeranganHama::class,
|
||||
'penyakit_tanaman' => PenyakitTanaman::class,
|
||||
'keparahan_serangan' => KeparahanSerangan::class,
|
||||
'jenis_hama' => 'array',
|
||||
'jenis_penyakit' => 'array'
|
||||
];
|
||||
|
||||
public function komoditas()
|
||||
|
||||
@@ -35,24 +35,25 @@ return new class extends Migration
|
||||
$table->string('cara_pemberian_pupuk')->nullable();
|
||||
// 2.5 Pengendalian Organisme Pengganggu Tumbuhan (OPT)
|
||||
$table->integer('metode_penanganan_opt')->nullable();
|
||||
$table->string('jenis_hama')->nullable();
|
||||
$table->string('jenis_penyakit')->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->integer('volume_obat_obatan_permusim')->nullable();
|
||||
$table->decimal('volume_obat_obatan_padat_permusim', 10, 2)->nullable();
|
||||
$table->decimal('volume_obat_obatan_cair_permusim', 10, 2)->nullable();
|
||||
// 2.6 Sanitasi Kebun
|
||||
$table->integer('frekuensi_sanitasi_lahan')->nullable();
|
||||
$table->string('penanganan_sisa_buah_ranting')->nullable();
|
||||
$table->integer('penanganan_gulma')->nullable();
|
||||
// 2.7 Pengelolaan Air & irigasi
|
||||
$table->integer('kondisi_pengelolaan_air')->nullable();
|
||||
$table->string('sumber_air')->nullable();
|
||||
$table->jsonb('sumber_air')->nullable();
|
||||
$table->string('sistem_irigasi')->nullable();
|
||||
$table->string('kebutuhan_air_permusim')->nullable();
|
||||
|
||||
// 2.8 Praktik konservasi tanah
|
||||
$table->string('kebiasaan_konservasi_tanah')->nullable();
|
||||
$table->jsonb('kebiasaan_konservasi_tanah')->nullable();
|
||||
$table->string('pemahaman_manfaat_konservasi_tanah')->nullable();
|
||||
$table->integer('nilai_konservasi_tanah')->nullable();
|
||||
|
||||
@@ -99,7 +100,7 @@ return new class extends Migration
|
||||
$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_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();
|
||||
|
||||
@@ -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,25 @@
|
||||
<?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('master_komoditas', function (Blueprint $table) {
|
||||
$table->boolean('is_utama')->default(false);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('master_komoditas', function (Blueprint $table) {
|
||||
$table->dropColumn('is_utama');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
<?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('master_jenis_tanaman', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('nama');
|
||||
|
||||
$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('jenis_tanamen');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,57 @@
|
||||
<?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('map_lahan', function (Blueprint $table) {
|
||||
if (! Schema::hasColumn('map_lahan', 'jenis_hama')) {
|
||||
$table->jsonb('jenis_hama')->nullable();
|
||||
}
|
||||
|
||||
if (! Schema::hasColumn('map_lahan', 'jenis_penyakit')) {
|
||||
$table->jsonb('jenis_penyakit')->nullable();
|
||||
}
|
||||
});
|
||||
|
||||
Schema::table('map_tanaman', function (Blueprint $table) {
|
||||
if (! Schema::hasColumn('map_tanaman', 'jenis_hama')) {
|
||||
$table->jsonb('jenis_hama')->nullable();
|
||||
}
|
||||
|
||||
if (! Schema::hasColumn('map_tanaman', 'jenis_penyakit')) {
|
||||
$table->jsonb('jenis_penyakit')->nullable();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('map_lahan', function (Blueprint $table) {
|
||||
if (Schema::hasColumn('map_lahan', 'jenis_hama')) {
|
||||
$table->dropColumn('jenis_hama');
|
||||
}
|
||||
|
||||
if (Schema::hasColumn('map_lahan', 'jenis_penyakit')) {
|
||||
$table->dropColumn('jenis_penyakit');
|
||||
}
|
||||
});
|
||||
|
||||
Schema::table('map_tanaman', function (Blueprint $table) {
|
||||
if (Schema::hasColumn('map_tanaman', 'jenis_hama')) {
|
||||
$table->dropColumn('jenis_hama');
|
||||
}
|
||||
|
||||
if (Schema::hasColumn('map_tanaman', 'jenis_penyakit')) {
|
||||
$table->dropColumn('jenis_penyakit');
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -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,38 @@
|
||||
<?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_pengurus', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('uid')->unique();
|
||||
$table->string('nama');
|
||||
$table->boolean('jenis_kelamin');
|
||||
$table->string('tempat_lahir')->nullable();
|
||||
$table->date('tanggal_lahir');
|
||||
$table->string('pendidikan_terakhir')->nullable();
|
||||
$table->text('alamat')->nullable();
|
||||
$table->foreignId('desa_kelurahan_id')->nullable()->constrained('master_desa_kelurahan')->onDelete('set null');
|
||||
$table->string('nomor_telepon')->nullable();
|
||||
$table->string('jabatan');
|
||||
$table->string('jabatan_lainnya')->nullable();
|
||||
$table->unsignedSmallInteger('lama_menjabat_tahun')->nullable();
|
||||
$table->date('tanggal_mulai_menjabat');
|
||||
$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_pengurus');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
<?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('bumdes_pengurus', function (Blueprint $table) {
|
||||
if (! Schema::hasColumn('bumdes_pengurus', 'pendidikan_terakhir')) {
|
||||
$table->string('pendidikan_terakhir')->nullable()->after('tanggal_lahir');
|
||||
}
|
||||
|
||||
if (! Schema::hasColumn('bumdes_pengurus', 'jabatan_lainnya')) {
|
||||
$table->string('jabatan_lainnya')->nullable()->after('jabatan');
|
||||
}
|
||||
|
||||
if (! Schema::hasColumn('bumdes_pengurus', 'lama_menjabat_tahun')) {
|
||||
$table->unsignedSmallInteger('lama_menjabat_tahun')->nullable()->after('jabatan_lainnya');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('bumdes_pengurus', function (Blueprint $table) {
|
||||
if (Schema::hasColumn('bumdes_pengurus', 'lama_menjabat_tahun')) {
|
||||
$table->dropColumn('lama_menjabat_tahun');
|
||||
}
|
||||
|
||||
if (Schema::hasColumn('bumdes_pengurus', 'jabatan_lainnya')) {
|
||||
$table->dropColumn('jabatan_lainnya');
|
||||
}
|
||||
|
||||
if (Schema::hasColumn('bumdes_pengurus', 'pendidikan_terakhir')) {
|
||||
$table->dropColumn('pendidikan_terakhir');
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
<?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->tinyInteger('tingkat_kepercayaan_pembeli_baru')->nullable()->after('sumber_info_premium');
|
||||
$table->text('alasan_kepercayaan_pembeli_baru')->nullable()->after('tingkat_kepercayaan_pembeli_baru');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('akses_pasar', function (Blueprint $table) {
|
||||
$table->dropColumn(['tingkat_kepercayaan_pembeli_baru', 'alasan_kepercayaan_pembeli_baru']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -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');
|
||||
}
|
||||
};
|
||||
+38
@@ -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'
|
||||
]);
|
||||
});
|
||||
}
|
||||
};
|
||||
+28
@@ -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',
|
||||
]);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -103,3 +103,85 @@
|
||||
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; }
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 5.4 KiB |
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 |
@@ -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',
|
||||
|
||||
@@ -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
|
||||
},
|
||||
|
||||
@@ -14,21 +14,21 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
username: {
|
||||
validators: {
|
||||
notEmpty: {
|
||||
message: 'Please enter username'
|
||||
message: 'Silakan masukkan username'
|
||||
},
|
||||
stringLength: {
|
||||
min: 6,
|
||||
message: 'Username must be more than 6 characters'
|
||||
message: 'Username harus terdiri dari minimal 6 karakter'
|
||||
}
|
||||
}
|
||||
},
|
||||
email: {
|
||||
validators: {
|
||||
notEmpty: {
|
||||
message: 'Please enter your email'
|
||||
message: 'Silakan masukkan email Anda'
|
||||
},
|
||||
emailAddress: {
|
||||
message: 'Please enter a valid email address'
|
||||
message: 'Silakan masukkan email yang valid'
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -57,22 +57,22 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
'confirm-password': {
|
||||
validators: {
|
||||
notEmpty: {
|
||||
message: 'Please confirm password'
|
||||
message: 'Silakan konfirmasi password Anda'
|
||||
},
|
||||
identical: {
|
||||
compare: () => formAuthentication.querySelector('[name="password"]').value,
|
||||
message: 'The password and its confirmation do not match'
|
||||
message: 'Password tidak cocok dengan konfirmasi password'
|
||||
},
|
||||
stringLength: {
|
||||
min: 6,
|
||||
message: 'Password must be more than 6 characters'
|
||||
message: 'Password harus terdiri dari minimal 6 karakter'
|
||||
}
|
||||
}
|
||||
},
|
||||
terms: {
|
||||
validators: {
|
||||
notEmpty: {
|
||||
message: 'Please agree to terms & conditions'
|
||||
message: 'Silakan terima syarat dan ketentuan kami'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -111,15 +126,20 @@
|
||||
<!-- endbuild -->
|
||||
|
||||
<!-- Vendors JS -->
|
||||
<script src="{{ asset('assets/vendor/libs/@form-validation/popular.js') }}"></script>
|
||||
<!-- <script src="{{ asset('assets/vendor/libs/@form-validation/popular.js') }}"></script>
|
||||
<script src="{{ asset('assets/vendor/libs/@form-validation/bootstrap5.js') }}"></script>
|
||||
<script src="{{ asset('assets/vendor/libs/@form-validation/auto-focus.js') }}"></script>
|
||||
<script src="{{ asset('assets/vendor/libs/@form-validation/auto-focus.js') }}"></script> -->
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.5/jquery.validate.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.5/additional-methods.min.js"></script>
|
||||
|
||||
<!-- Main JS -->
|
||||
|
||||
<script src="{{ asset('assets/js/main.js') }}"></script>
|
||||
|
||||
<!-- Page JS -->
|
||||
<script src="{{ asset('assets/js/pages-auth.js') }}"></script>
|
||||
<!-- <script src="{{ asset('assets/js/pages-auth.js') }}"></script> -->
|
||||
|
||||
@stack('scripts')
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -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">
|
||||
@@ -87,3 +75,44 @@
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
$('#formAuthentication').validate({
|
||||
rules: {
|
||||
email_username: {
|
||||
required: true,
|
||||
minlength: 6,
|
||||
},
|
||||
password: {
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
messages: {
|
||||
email_username: {
|
||||
required: 'Email atau Nama Pengguna wajib diisi',
|
||||
minlength: 'Email atau Nama Pengguna minimal 6 karakter',
|
||||
},
|
||||
password: {
|
||||
required: 'Password wajib diisi',
|
||||
},
|
||||
},
|
||||
errorElement: 'div',
|
||||
errorPlacement: function (error, element) {
|
||||
error.addClass('invalid-feedback');
|
||||
element.closest('.mb-6, .form-control-validation').append(error);
|
||||
},
|
||||
highlight: function (element) {
|
||||
$(element).addClass('is-invalid');
|
||||
},
|
||||
unhighlight: function (element) {
|
||||
$(element).removeClass('is-invalid');
|
||||
},
|
||||
submitHandler: function (form) {
|
||||
form.submit();
|
||||
}
|
||||
})
|
||||
})
|
||||
</script>
|
||||
@endpush
|
||||
@@ -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">
|
||||
@@ -113,7 +85,7 @@
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<div class="divider my-6">
|
||||
<!-- <div class="divider my-6">
|
||||
<div class="divider-text">atau</div>
|
||||
</div>
|
||||
|
||||
@@ -133,7 +105,72 @@
|
||||
<a href="javascript:;" class="btn btn-icon rounded-circle btn-text-google-plus">
|
||||
<i class="icon-base ti tabler-brand-google-filled icon-20px"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
$('#formAuthentication').validate({
|
||||
rules: {
|
||||
username: {
|
||||
required: true,
|
||||
minlength: 6,
|
||||
},
|
||||
email: {
|
||||
required: true,
|
||||
email: true,
|
||||
},
|
||||
password: {
|
||||
required: true,
|
||||
minlength: 8,
|
||||
},
|
||||
password_confirmation: {
|
||||
required: true,
|
||||
equalTo: '#password',
|
||||
},
|
||||
terms: {
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
messages: {
|
||||
username: {
|
||||
required: 'Username wajib diisi',
|
||||
minlength: 'Username minimal 6 karakter',
|
||||
},
|
||||
email: {
|
||||
required: 'Email wajib diisi',
|
||||
email: 'Format email tidak sesuai',
|
||||
},
|
||||
password: {
|
||||
required: 'Kata Sandi wajib diisi',
|
||||
},
|
||||
password_confirmation: {
|
||||
required: 'Konfirmasi Kata Sandi wajib diisi',
|
||||
equalTo: 'Konfirmasi Kata Sandi tidak sesuai',
|
||||
minlength: 'Password minimal 8 karakter',
|
||||
},
|
||||
terms: {
|
||||
required: 'Anda harus menyetujui ketentuan layanan',
|
||||
},
|
||||
},
|
||||
errorElement: 'div',
|
||||
errorPlacement: function (error, element) {
|
||||
error.addClass('invalid-feedback');
|
||||
element.closest('.mb-6, .form-control-validation').append(error);
|
||||
},
|
||||
highlight: function (element) {
|
||||
$(element).addClass('is-invalid');
|
||||
},
|
||||
unhighlight: function (element) {
|
||||
$(element).removeClass('is-invalid');
|
||||
},
|
||||
submitHandler: function (form) {
|
||||
form.submit();
|
||||
}
|
||||
})
|
||||
})
|
||||
</script>
|
||||
@endpush
|
||||
|
||||
@@ -0,0 +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
|
||||
@@ -0,0 +1,280 @@
|
||||
@php
|
||||
$prefix = $mode . '-bumdes';
|
||||
$currentProvinsiId = old('provinsi_id', $pengurus?->desaKelurahan?->kecamatan?->kabupatenKota?->provinsi_id);
|
||||
$currentKabupaten = $pengurus?->desaKelurahan?->kecamatan?->kabupatenKota;
|
||||
$currentKecamatan = $pengurus?->desaKelurahan?->kecamatan;
|
||||
$currentDesa = $pengurus?->desaKelurahan;
|
||||
$currentJabatan = $pengurus?->jabatan instanceof \App\Enums\Bumdes\JabatanBumdes ? $pengurus->jabatan->value : $pengurus?->jabatan;
|
||||
$currentPendidikan = $pengurus?->pendidikan_terakhir instanceof \App\Enums\Pendidikan ? $pengurus->pendidikan_terakhir->value : $pengurus?->pendidikan_terakhir;
|
||||
@endphp
|
||||
|
||||
<h5 class="pb-4 border-bottom mb-4">Data Pribadi</h5>
|
||||
<div class="row">
|
||||
<div class="col-md-3 mb-4 form-control-validation">
|
||||
<label class="form-label" for="{{ $prefix }}-nama">Nama Pengurus BUMDes<span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control @error('nama') is-invalid @enderror" id="{{ $prefix }}-nama" name="nama" placeholder="Contoh: John Doe" value="{{ old('nama', $pengurus?->nama) }}" />
|
||||
@error('nama')<div class="error invalid-feedback">{{ $message }}</div>@enderror
|
||||
</div>
|
||||
<div class="col-md-3 mb-4 form-control-validation">
|
||||
<label class="form-label" for="{{ $prefix }}-jenis-kelamin">Jenis Kelamin<span class="text-danger">*</span></label>
|
||||
<select class="form-control @error('jenis_kelamin') is-invalid @enderror" id="{{ $prefix }}-jenis-kelamin" name="jenis_kelamin">
|
||||
<option value="">Pilih Jenis Kelamin</option>
|
||||
<option value="1" {{ old('jenis_kelamin', $pengurus?->jenis_kelamin) == '1' ? 'selected' : '' }}>Laki-laki</option>
|
||||
<option value="0" {{ old('jenis_kelamin', $pengurus?->jenis_kelamin) == '0' ? 'selected' : '' }}>Perempuan</option>
|
||||
</select>
|
||||
@error('jenis_kelamin')<div class="error invalid-feedback">{{ $message }}</div>@enderror
|
||||
</div>
|
||||
<div class="col-md-3 mb-4 form-control-validation">
|
||||
<label class="form-label" for="{{ $prefix }}-tempat-lahir">Tempat Lahir<span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control @error('tempat_lahir') is-invalid @enderror" id="{{ $prefix }}-tempat-lahir" name="tempat_lahir" placeholder="Contoh: Ngada" value="{{ old('tempat_lahir', $pengurus?->tempat_lahir) }}" />
|
||||
@error('tempat_lahir')<div class="error invalid-feedback">{{ $message }}</div>@enderror
|
||||
</div>
|
||||
<div class="col-md-3 mb-4 form-control-validation">
|
||||
<label class="form-label" for="{{ $prefix }}-tanggal-lahir">Tanggal Lahir<span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control @error('tanggal_lahir') is-invalid @enderror" id="{{ $prefix }}-tanggal-lahir" name="tanggal_lahir" value="{{ old('tanggal_lahir', $pengurus?->tanggal_lahir?->format('d-m-Y')) }}" />
|
||||
@error('tanggal_lahir')<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="{{ $prefix }}-pendidikan-terakhir">Pendidikan Terakhir<span class="text-danger">*</span></label>
|
||||
<select class="form-control @error('pendidikan_terakhir') is-invalid @enderror" id="{{ $prefix }}-pendidikan-terakhir" name="pendidikan_terakhir">
|
||||
<option value="">Pilih Pendidikan Terakhir</option>
|
||||
@foreach($pendidikan as $value)
|
||||
<option value="{{ $value['value'] }}" {{ old('pendidikan_terakhir', $currentPendidikan) == $value['value'] ? 'selected' : '' }}>{{ $value['label'] }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@error('pendidikan_terakhir')<div class="error invalid-feedback">{{ $message }}</div>@enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h5 class="pb-4 border-bottom mb-4 mt-4">Peran Kepengurusan BUMDes</h5>
|
||||
<div class="row">
|
||||
<div class="col-md-3 mb-4 form-control-validation">
|
||||
<label class="form-label" for="{{ $prefix }}-jabatan">Jabatan/Peran<span class="text-danger">*</span></label>
|
||||
<select class="form-control @error('jabatan') is-invalid @enderror" id="{{ $prefix }}-jabatan" name="jabatan">
|
||||
<option value="">Pilih Jabatan/Peran</option>
|
||||
@foreach($jabatan as $value)
|
||||
<option value="{{ $value['value'] }}" {{ old('jabatan', $currentJabatan) == $value['value'] ? 'selected' : '' }}>{{ $value['label'] }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@error('jabatan')<div class="error invalid-feedback">{{ $message }}</div>@enderror
|
||||
</div>
|
||||
<div class="col-md-3 mb-4 form-control-validation {{ old('jabatan', $currentJabatan) === \App\Enums\Bumdes\JabatanBumdes::LAINNYA->value ? '' : 'd-none' }}" id="{{ $prefix }}-jabatan-lainnya-wrapper">
|
||||
<label class="form-label" for="{{ $prefix }}-jabatan-lainnya">Jabatan Lainnya<span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control @error('jabatan_lainnya') is-invalid @enderror" id="{{ $prefix }}-jabatan-lainnya" name="jabatan_lainnya" placeholder="Contoh: Kepala Unit Usaha" value="{{ old('jabatan_lainnya', $pengurus?->jabatan_lainnya) }}" />
|
||||
@error('jabatan_lainnya')<div class="error invalid-feedback">{{ $message }}</div>@enderror
|
||||
</div>
|
||||
<div class="col-md-3 mb-4 form-control-validation">
|
||||
<label class="form-label" for="{{ $prefix }}-lama-menjabat">Lama Menjabat (Tahun)<span class="text-danger">*</span></label>
|
||||
<input type="number" min="0" max="80" class="form-control @error('lama_menjabat_tahun') is-invalid @enderror" id="{{ $prefix }}-lama-menjabat" name="lama_menjabat_tahun" placeholder="Contoh: 2" value="{{ old('lama_menjabat_tahun', $pengurus?->lama_menjabat_tahun) }}" />
|
||||
@error('lama_menjabat_tahun')<div class="error invalid-feedback">{{ $message }}</div>@enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h5 class="pb-4 border-bottom mb-4 mt-4">Domisili dan Kontak</h5>
|
||||
<div class="row">
|
||||
<div class="col-md-12 mb-4 form-control-validation">
|
||||
<label class="form-label" for="{{ $prefix }}-alamat">Alamat<span class="text-danger">*</span></label>
|
||||
<textarea class="form-control @error('alamat') is-invalid @enderror" id="{{ $prefix }}-alamat" name="alamat" rows="2" placeholder="Contoh: Jl. Nama Jalan, Nama Dusun">{{ old('alamat', $pengurus?->alamat) }}</textarea>
|
||||
@error('alamat')<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="{{ $prefix }}-provinsi">Provinsi<span class="text-danger">*</span></label>
|
||||
<select class="form-control @error('provinsi_id') is-invalid @enderror" id="{{ $prefix }}-provinsi" name="provinsi_id">
|
||||
<option value="">Pilih Provinsi</option>
|
||||
@foreach($provinsi as $p)
|
||||
<option value="{{ $p->id }}" {{ $currentProvinsiId == $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="{{ $prefix }}-kabupaten">Kabupaten/Kota<span class="text-danger">*</span></label>
|
||||
<select class="form-control @error('kabupaten_id') is-invalid @enderror" id="{{ $prefix }}-kabupaten" name="kabupaten_id">
|
||||
<option value="">Pilih Kabupaten/Kota</option>
|
||||
@if($currentKabupaten)
|
||||
<option value="{{ $currentKabupaten->id }}" selected>{{ $currentKabupaten->nama }}</option>
|
||||
@endif
|
||||
</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="{{ $prefix }}-kecamatan">Kecamatan<span class="text-danger">*</span></label>
|
||||
<select class="form-control @error('kecamatan_id') is-invalid @enderror" id="{{ $prefix }}-kecamatan" name="kecamatan_id">
|
||||
<option value="">Pilih Kecamatan</option>
|
||||
@if($currentKecamatan)
|
||||
<option value="{{ $currentKecamatan->id }}" selected>{{ $currentKecamatan->nama }}</option>
|
||||
@endif
|
||||
</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="{{ $prefix }}-desa">Desa/Kelurahan<span class="text-danger">*</span></label>
|
||||
<select class="form-control @error('desa_kelurahan_id') is-invalid @enderror" id="{{ $prefix }}-desa" name="desa_kelurahan_id">
|
||||
<option value="">Pilih Desa/Kelurahan</option>
|
||||
@if($currentDesa)
|
||||
<option value="{{ $currentDesa->id }}" selected>{{ $currentDesa->nama }}</option>
|
||||
@endif
|
||||
</select>
|
||||
@error('desa_kelurahan_id')<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="{{ $prefix }}-nomor-telepon">No HP</label>
|
||||
<input type="text" class="form-control @error('nomor_telepon') is-invalid @enderror" id="{{ $prefix }}-nomor-telepon" name="nomor_telepon" placeholder="Contoh: 081234567890" value="{{ old('nomor_telepon', $pengurus?->nomor_telepon) }}" minlength="11" maxlength="13" />
|
||||
@error('nomor_telepon')<div class="error invalid-feedback">{{ $message }}</div>@enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
'use strict';
|
||||
$(document).ready(function () {
|
||||
const prefix = '#{{ $prefix }}';
|
||||
const lainnyaValue = '{{ \App\Enums\Bumdes\JabatanBumdes::LAINNYA->value }}';
|
||||
|
||||
$(`${prefix}-jenis-kelamin, ${prefix}-pendidikan-terakhir, ${prefix}-jabatan`).select2();
|
||||
$(`${prefix}-tanggal-lahir`).flatpickr({
|
||||
dateFormat: 'd-m-Y'
|
||||
});
|
||||
|
||||
function toggleJabatanLainnya() {
|
||||
const isLainnya = $(`${prefix}-jabatan`).val() === lainnyaValue;
|
||||
$(`${prefix}-jabatan-lainnya-wrapper`).toggleClass('d-none', !isLainnya);
|
||||
|
||||
if (!isLainnya) {
|
||||
$(`${prefix}-jabatan-lainnya`).val('');
|
||||
}
|
||||
}
|
||||
|
||||
$(`${prefix}-jabatan`).on('change', toggleJabatanLainnya);
|
||||
toggleJabatanLainnya();
|
||||
|
||||
$(`${prefix}-provinsi`).select2().change(function(e) {
|
||||
e.preventDefault();
|
||||
const provinsiId = $(this).val();
|
||||
$(`${prefix}-kabupaten`).prop('disabled', !provinsiId).val('').trigger('change');
|
||||
$(`${prefix}-kecamatan`).prop('disabled', true).val('').trigger('change');
|
||||
$(`${prefix}-desa`).prop('disabled', true).val('').trigger('change');
|
||||
|
||||
if (provinsiId) {
|
||||
$.ajax({
|
||||
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>');
|
||||
data.forEach(function(kabupaten) {
|
||||
$(`${prefix}-kabupaten`).append('<option value="' + kabupaten.id + '">' + kabupaten.nama + '</option>');
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$(`${prefix}-kabupaten`).select2().change(function(e) {
|
||||
e.preventDefault();
|
||||
const kabupatenId = $(this).val();
|
||||
$(`${prefix}-kecamatan`).prop('disabled', !kabupatenId).val('').trigger('change');
|
||||
$(`${prefix}-desa`).prop('disabled', true).val('').trigger('change');
|
||||
|
||||
if (kabupatenId) {
|
||||
$.ajax({
|
||||
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>');
|
||||
data.forEach(function(kecamatan) {
|
||||
$(`${prefix}-kecamatan`).append('<option value="' + kecamatan.id + '">' + kecamatan.nama + '</option>');
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$(`${prefix}-kecamatan`).select2().change(function(e) {
|
||||
e.preventDefault();
|
||||
const kecamatanId = $(this).val();
|
||||
$(`${prefix}-desa`).prop('disabled', !kecamatanId).val('').trigger('change');
|
||||
|
||||
if (kecamatanId) {
|
||||
$.ajax({
|
||||
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>');
|
||||
data.forEach(function(desa) {
|
||||
$(`${prefix}-desa`).append('<option value="' + desa.id + '">' + desa.nama + '</option>');
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$(`${prefix}-desa`).select2();
|
||||
|
||||
$('#{{ $mode }}BumdesForm').validate({
|
||||
rules: {
|
||||
nama: { required: true },
|
||||
jenis_kelamin: { required: true },
|
||||
tempat_lahir: { required: true },
|
||||
tanggal_lahir: { required: true },
|
||||
pendidikan_terakhir: { required: true },
|
||||
jabatan: { required: true },
|
||||
jabatan_lainnya: {
|
||||
required: function() {
|
||||
return $(`${prefix}-jabatan`).val() === lainnyaValue;
|
||||
}
|
||||
},
|
||||
lama_menjabat_tahun: { required: true, digits: true, min: 0, max: 80 },
|
||||
alamat: { required: true },
|
||||
provinsi_id: { required: true },
|
||||
kabupaten_id: { required: true },
|
||||
kecamatan_id: { required: true },
|
||||
desa_kelurahan_id: { required: true },
|
||||
nomor_telepon: { required: false, digits: true, minlength: 11, maxlength: 13 }
|
||||
},
|
||||
messages: {
|
||||
nama: { required: 'Nama pengurus BUMDes wajib diisi' },
|
||||
jenis_kelamin: { required: 'Jenis kelamin wajib dipilih' },
|
||||
tempat_lahir: { required: 'Tempat lahir wajib diisi' },
|
||||
tanggal_lahir: { required: 'Tanggal lahir wajib diisi' },
|
||||
pendidikan_terakhir: { required: 'Pendidikan terakhir wajib dipilih' },
|
||||
jabatan: { required: 'Jabatan/peran wajib dipilih' },
|
||||
jabatan_lainnya: { required: 'Jabatan lainnya wajib diisi' },
|
||||
lama_menjabat_tahun: {
|
||||
required: 'Lama menjabat wajib diisi',
|
||||
digits: 'Lama menjabat harus berupa angka tahun',
|
||||
min: 'Lama menjabat tidak boleh kurang dari 0 tahun',
|
||||
max: 'Lama menjabat tidak boleh lebih dari 80 tahun'
|
||||
},
|
||||
alamat: { required: 'Alamat wajib diisi' },
|
||||
provinsi_id: { required: 'Provinsi wajib dipilih' },
|
||||
kabupaten_id: { required: 'Kabupaten/Kota wajib dipilih' },
|
||||
kecamatan_id: { required: 'Kecamatan wajib dipilih' },
|
||||
desa_kelurahan_id: { required: 'Desa/Kelurahan wajib dipilih' },
|
||||
nomor_telepon: {
|
||||
digits: 'Nomor telepon harus berupa angka',
|
||||
minlength: 'Nomor telepon minimal 11 digit',
|
||||
maxlength: 'Nomor telepon maksimal 13 digit'
|
||||
}
|
||||
},
|
||||
errorElement: 'div',
|
||||
errorPlacement: function (error, element) {
|
||||
error.addClass('invalid-feedback');
|
||||
element.closest('.form-control-validation').append(error);
|
||||
},
|
||||
highlight: function (element) {
|
||||
$(element).addClass('is-invalid');
|
||||
},
|
||||
unhighlight: function (element) {
|
||||
$(element).removeClass('is-invalid');
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
@@ -0,0 +1,34 @@
|
||||
@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('bumdes.profil.header', ['pengurus' => null])
|
||||
|
||||
<div class="card mb-6">
|
||||
<div class="card-header border-bottom">
|
||||
<h5 class="card-title mb-0">Tambah Profil Pengurus BUMDes</h5>
|
||||
</div>
|
||||
<div class="card-body pt-6">
|
||||
<form id="addBumdesForm" action="{{ route('bumdes.store') }}" method="POST">
|
||||
@csrf
|
||||
@include('bumdes.profil._form', ['pengurus' => null, 'mode' => 'add'])
|
||||
|
||||
<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 Profil</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
@@ -0,0 +1,39 @@
|
||||
@extends('layout.main')
|
||||
|
||||
@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])
|
||||
|
||||
<div class="card mb-6">
|
||||
<div class="card-header border-bottom">
|
||||
<h5 class="card-title mb-0">Edit Profil Pengurus BUMDes</h5>
|
||||
</div>
|
||||
<div class="card-body pt-6">
|
||||
<form id="editBumdesForm" action="{{ route('bumdes.update', $pengurus->id) }}" method="POST">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
@include('bumdes.profil._form', ['pengurus' => $pengurus, 'mode' => 'edit'])
|
||||
|
||||
<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 Profil</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
@@ -0,0 +1,162 @@
|
||||
<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
|
||||
$jenisKelamin = isset($pengurus)
|
||||
? ($pengurus->jenis_kelamin ? 'Laki-laki' : 'Perempuan')
|
||||
: 'Jenis Kelamin';
|
||||
$jabatanLabel = isset($pengurus) ? $pengurus->nama_jabatan : 'Jabatan/Peran';
|
||||
$domisili = isset($pengurus) && $pengurus->desaKelurahan
|
||||
? $pengurus->desaKelurahan->nama . ', ' . $pengurus->desaKelurahan->kecamatan?->nama . ', ' . $pengurus->desaKelurahan->kecamatan?->kabupatenKota?->nama . ', ' . $pengurus->desaKelurahan->kecamatan?->kabupatenKota?->provinsi?->nama
|
||||
: 'Desa/Kelurahan, Kecamatan, Kabupaten/Kota, Provinsi';
|
||||
@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 BUMDes</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;">{{ isset($pengurus) ? $pengurus->nama : 'Tambah Pengurus BUMDes Baru' }}</h6>
|
||||
<span class="text-muted" style="font-size: 0.75rem;">{{ $jenisKelamin }}</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;">Domisili</h6>
|
||||
<p class="mb-0 text-muted" style="font-size: 0.75rem;">Alamat: {{ isset($pengurus) ? $pengurus->alamat : 'Alamat' }}</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;">Jenis Kelamin</p>
|
||||
<h5 class="mb-0">{{ $jenisKelamin }}</h5>
|
||||
<p class="mb-0" style="font-size: 0.75rem;">
|
||||
<span class="me-1">{{ isset($pengurus) ? ($pengurus->usia . ' tahun') : 'Belum terisi' }}</span>
|
||||
</p>
|
||||
</div>
|
||||
<span class="avatar">
|
||||
<span class="avatar-initial rounded">
|
||||
<i class="icon-base ti tabler-users 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;">Jabatan</p>
|
||||
<h5 class="mb-0">{{ $jabatanLabel }}</h5>
|
||||
<p class="mb-0" style="font-size: 0.75rem;">
|
||||
<span class="me-1">{{ isset($pengurus) ? $pengurus->lama_menjabat : 'Belum terisi' }}</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;">Domisili</p>
|
||||
<h5 class="mb-0">{{ isset($pengurus) ? ($pengurus->desaKelurahan?->nama ?? '-') : '-' }}</h5>
|
||||
<p class="mb-0" style="font-size: 0.75rem;">
|
||||
<span class="me-1">{{ isset($pengurus) ? ($pengurus->desaKelurahan?->kecamatan?->nama ?? '-') : 'Belum terisi' }}</span>
|
||||
</p>
|
||||
</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 Domisili</p>
|
||||
<h6 class="mb-0">{{ isset($pengurus) ? ($pengurus->alamat ?: '-') : '-' }}</h6>
|
||||
<p class="mb-0 text-muted" style="font-size: 0.75rem;">{{ $domisili }}</p>
|
||||
</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">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {{ request()->routeIs('bumdes.create') || request()->routeIs('bumdes.edit') ? 'active' : '' }}" href="{{ isset($pengurus) ? route('bumdes.edit', $pengurus->id) : route('bumdes.create') }}">
|
||||
<i class="icon-base ti tabler-user-check icon-sm"></i>Profil
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
@if(isset($pengurus))
|
||||
<a class="nav-link {{ request()->routeIs('bumdes.kelembagaan-aktor.*') ? 'active' : '' }}" href="{{ route('bumdes.kelembagaan-aktor.index', $pengurus->id) }}">
|
||||
<i class="icon-base ti tabler-building-community icon-sm"></i>Kelembagaan dan Aktor
|
||||
</a>
|
||||
@else
|
||||
<a class="nav-link" href="#" disabled data-bs-original-title="Kelembagaan dan Aktor, isi profil terlebih dahulu" data-bs-toggle="tooltip" data-bs-placement="top">
|
||||
<i class="icon-base ti tabler-building-community icon-sm"></i>Kelembagaan dan Aktor
|
||||
</a>
|
||||
@endif
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -0,0 +1,158 @@
|
||||
@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 BUMDes"
|
||||
count="{{ $totalPengurus }}"
|
||||
subtitle="{{ $totalLakiLaki }} Laki-laki, {{ $totalPerempuan }} Perempuan"
|
||||
icon="user-check"
|
||||
color="primary"
|
||||
/>
|
||||
</div>
|
||||
<div class="w-25">
|
||||
<x-summary-card
|
||||
title="Desa/Kelurahan"
|
||||
count="{{ $totalDesaKelurahan }}"
|
||||
subtitle="desa/kelurahan memiliki data pengurus"
|
||||
icon="map-pin"
|
||||
color="success"
|
||||
/>
|
||||
</div>
|
||||
<div class="w-25">
|
||||
<x-summary-card
|
||||
title="Jabatan/Peran"
|
||||
count="{{ $totalJabatan }}"
|
||||
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 BUMDes</h5>
|
||||
</div>
|
||||
|
||||
<div class="card-body border-bottom pt-4">
|
||||
<form method="GET" action="{{ route('bumdes.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('bumdes.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</th>
|
||||
<th>Jenis Kelamin</th>
|
||||
<th>Usia</th>
|
||||
<th>Desa/Kelurahan</th>
|
||||
<th>Jabatan/Peran</th>
|
||||
<th>Lama Menjabat</th>
|
||||
@if($showEnumerator)
|
||||
<th>Nama Enumerator</th>
|
||||
@endif
|
||||
<th>Aksi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($pengurus as $item)
|
||||
<tr>
|
||||
<td>{{ ($pengurus->firstItem() ?? 0) + $loop->index }}</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->usia }} tahun</span></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->nama_jabatan }}</td>
|
||||
<td><span class="text-muted">{{ $item->lama_menjabat }}</span></td>
|
||||
@if($showEnumerator)
|
||||
<td><span class="text-muted">{{ $item->creator?->name ?? '-' }}</span></td>
|
||||
@endif
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
<a href="{{ route('bumdes.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 }}"><i class="ti tabler-trash ti-md"></i></a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="{{ $showEnumerator ? 9 : 8 }}" 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">
|
||||
{{ $pengurus->appends(request()->query())->links('pagination::bootstrap-5') }}
|
||||
</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: `Profile "${name}" akan dihapus secara permanen!`,
|
||||
icon: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: 'Ya, Hapus!',
|
||||
customClass: { confirmButton: 'btn btn-primary me-3', cancelButton: 'btn btn-label-secondary' },
|
||||
buttonsStyling: false
|
||||
}).then(function (result) {
|
||||
if (result.value) {
|
||||
$.ajax({
|
||||
url: '{{ route('bumdes.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
|
||||
@@ -4,7 +4,7 @@
|
||||
<div class="card-body d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<p class="text-muted fw-medium mb-1">{{ $title }}</p>
|
||||
<h3 class="text-dark fw-bold mb-1">{{ $count }}</h3>
|
||||
<h3 class="text-dark fw-bold mb-1">{!! $count !!}</h3>
|
||||
<small class="text-muted">{{ $subtitle }}</small>
|
||||
</div>
|
||||
<div class="badge bg-label-{{ $color }} p-2 rounded">
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
@extends('layout.main')
|
||||
|
||||
@section('content')
|
||||
<script>
|
||||
window.location.replace(@json(route('dashboard.karakteristik-petani', ['tab' => 'demografi-petani'])));
|
||||
</script>
|
||||
<div class="container-xxl flex-grow-1 container-p-y">
|
||||
<div class="alert alert-info mb-0">
|
||||
Mengalihkan ke Karakteristik Petani...
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
@@ -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 } }
|
||||
},
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
@extends('layout.main')
|
||||
|
||||
@section('content')
|
||||
<script>
|
||||
window.location.replace(@json(route('dashboard.karakteristik-petani', ['tab' => 'indeks-petani'])));
|
||||
</script>
|
||||
<div class="container-xxl flex-grow-1 container-p-y">
|
||||
<div class="alert alert-info mb-0">
|
||||
Mengalihkan ke Karakteristik Petani...
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
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';
|
||||
|
||||
@@ -0,0 +1,671 @@
|
||||
@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%;
|
||||
}
|
||||
}
|
||||
</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 & 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>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
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
|
||||
@@ -1,6 +1,35 @@
|
||||
@extends('layout.main')
|
||||
|
||||
@section('content')
|
||||
@php
|
||||
$totalPetani = $petaniSummary->sum('total');
|
||||
$segments = [
|
||||
[
|
||||
'name' => 'Penggerak',
|
||||
'range' => '4.0 - 5.0',
|
||||
'icon' => 'tabler-crown',
|
||||
'color' => 'success',
|
||||
],
|
||||
[
|
||||
'name' => 'Perintis',
|
||||
'range' => '3.0 - 3.9',
|
||||
'icon' => 'tabler-flag',
|
||||
'color' => 'info',
|
||||
],
|
||||
[
|
||||
'name' => 'Pemerhati',
|
||||
'range' => '2.0 - 2.9',
|
||||
'icon' => 'tabler-eye',
|
||||
'color' => 'warning',
|
||||
],
|
||||
[
|
||||
'name' => 'Pemula',
|
||||
'range' => '1.0 - 1.9',
|
||||
'icon' => 'tabler-seedling',
|
||||
'color' => 'danger',
|
||||
],
|
||||
];
|
||||
@endphp
|
||||
<div class="container-xxl flex-grow-1 container-p-y">
|
||||
<div class="row g-6">
|
||||
<div class="col-lg-3 col-sm-6">
|
||||
@@ -12,9 +41,9 @@
|
||||
<i class="icon-base ti tabler-user-check icon-28px"></i>
|
||||
</span>
|
||||
</div>
|
||||
<h4 class="mb-0">{{ $petaniSummary->sum('total') }}</h4>
|
||||
<h4 class="mb-0">{{ $totalPetani }}</h4>
|
||||
</div>
|
||||
<p class="mb-1">Petani</p>
|
||||
<p class="mb-1">Total Petani</p>
|
||||
<p class="mb-0">
|
||||
<span class="text-heading fw-medium me-2">{{ $petaniSummary->count('total') }}</span>
|
||||
<small class="text-body-secondary">komoditas utama</small>
|
||||
@@ -31,7 +60,7 @@
|
||||
<i class="icon-base ti tabler-trees icon-28px"></i>
|
||||
</span>
|
||||
</div>
|
||||
<h4 class="mb-0">{{ $lahanSummary->sum('luas_lahan') }}m<sup>2</sup></h4>
|
||||
<h4 class="mb-0">{{ number_format($lahanSummary->sum('luas_lahan') / 10000, 2, ',', '.') }} ha</h4>
|
||||
</div>
|
||||
<p class="mb-1">Lahan</p>
|
||||
<p class="mb-0">
|
||||
@@ -41,26 +70,21 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6 col-sm-12">
|
||||
<div class="card card-border-shadow-danger h-100">
|
||||
<div class="col-lg-3 col-sm-6">
|
||||
<div class="card card-border-shadow-info h-100">
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-center mb-2">
|
||||
<div class="avatar me-4">
|
||||
<span class="avatar-initial rounded bg-label-danger">
|
||||
<i class="icon-base ti tabler-seedling icon-28px"></i>
|
||||
<span class="avatar-initial rounded bg-label-warning">
|
||||
<i class="icon-base ti tabler-star icon-28px"></i>
|
||||
</span>
|
||||
</div>
|
||||
<h4 class="mb-0">{{ $tanamanSummary->sum('total') }}</h4>
|
||||
</div>
|
||||
<p class="mb-1">Tanaman Komoditas Utama</p>
|
||||
<div class="d-flex justify-content-between mb-2">
|
||||
@foreach($komoditas as $k)
|
||||
<p class="mb-0">
|
||||
<span class="text-heading fw-medium me-2">{{ $tanamanSummary->where('komoditas_id', $k->id)->sum('total') }}</span>
|
||||
<small class="text-body-danger">tanaman {{ $k->nama }}</small>
|
||||
</p>
|
||||
@endforeach
|
||||
<h4 class="mb-0">{{ number_format($skorAverage, 2, ',', '.') }}</h4>
|
||||
</div>
|
||||
<p class="mb-1">Rata-rata Indeks Kesiapan Adopsi</p>
|
||||
<p class="mb-0">
|
||||
<span class="text-heading fw-medium me-2">{{ $segmenAverage }}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -76,84 +100,38 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table class="table card-table table-border-top-0 table-border-bottom-0">
|
||||
<table class="table card-table table-border-top-0 table-border-bottom-0 data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th class="text-end pe-0">Petani</th>
|
||||
<th class="text-end pe-0">%</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
@foreach ($segments as $segment)
|
||||
@php
|
||||
$total = $skorSummary->where('segmen', $segment['name'])->sum('total');
|
||||
$persen = $totalPetani ? ($total / $totalPetani) * 100 : 0;
|
||||
@endphp
|
||||
|
||||
<tr>
|
||||
<td class="w-60 ps-0">
|
||||
<td>
|
||||
<div class="d-flex justify-content-start align-items-center">
|
||||
<div class="me-2">
|
||||
<i class="icon-base ti tabler-crown icon-lg text-heading"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h6 class="mb-0 fw-normal text-success">Penggerak (4.0 - 5.0)</h6>
|
||||
<!-- <small class="text-body-secondary text-wrap">Siap menjadi peserta agrolab utama dan panutan bagi petani lain</small> -->
|
||||
<i class="icon-base ti {{ $segment['icon'] }} icon-lg text-{{ $segment['color'] }}"></i>
|
||||
</div>
|
||||
<span class="text-{{ $segment['color'] }}">
|
||||
{{ $segment['name'] }}
|
||||
<small class="text-body-secondary">({{ $segment['range'] }})</small>
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-end pe-0">
|
||||
<span>{{ $skorSummary->where('segmentasi', 'Penggerak')->sum('total') }} petani</span>
|
||||
</td>
|
||||
<td class="text-end pe-0">
|
||||
<span>{{ number_format(($skorSummary->where('segmentasi', 'Penggerak')->sum('total') / $petaniSummary->sum('total')) * 100, 2) }}%</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="w-60 ps-0">
|
||||
<div class="d-flex justify-content-start align-items-center">
|
||||
<div class="me-2">
|
||||
<i class="icon-base ti tabler-flag icon-lg text-heading"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h6 class="mb-0 fw-normal text-info">Perintis (3.0 - 3.9)</h6>
|
||||
<!-- <small class="text-body-secondary text-wrap">Berpotensi berkembang menjadi Penggerak dengan dukungan yang tepat</small> -->
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-end pe-0">
|
||||
<span>{{ $skorSummary->where('segmentasi', 'Perintis')->sum('total') }} petani</span>
|
||||
</td>
|
||||
<td class="text-end pe-0">
|
||||
<span>{{ number_format(($skorSummary->where('segmentasi', 'Perintis')->sum('total') / $petaniSummary->sum('total')) * 100, 2) }}%</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="w-60 ps-0">
|
||||
<div class="d-flex justify-content-start align-items-center">
|
||||
<div class="me-2">
|
||||
<i class="icon-base ti tabler-eye icon-lg text-heading text-success"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h6 class="mb-0 fw-normal text-warning">Pemerhati (2.0 - 2.9)</h6>
|
||||
<!-- <small class="text-body-secondary text-wrap">Perlu melihat bukti hasil nyata dari Penggerak sebelum mau bergerak</small> -->
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-end pe-0">
|
||||
<span>{{ $skorSummary->where('segmentasi', 'Pemerhati')->sum('total') }} petani</span>
|
||||
</td>
|
||||
<td class="text-end pe-0">
|
||||
<span>{{ number_format(($skorSummary->where('segmentasi', 'Pemerhati')->sum('total') / $petaniSummary->sum('total')) * 100, 2) }}%</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="w-60 ps-0">
|
||||
<div class="d-flex justify-content-start align-items-center">
|
||||
<div class="me-2">
|
||||
<i class="icon-base ti tabler-seedling icon-lg text-heading"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h6 class="mb-0 fw-normal text-danger">Pemula (1.0 - 1.9)</h6>
|
||||
<!-- <small class="text-body-secondary text-wrap">Butuh penanganan hambatan dasar terlebih dahulu sebelum intervensi langsung</small> -->
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-end pe-0">
|
||||
<span>{{ $skorSummary->where('segmentasi', 'Pemula')->sum('total') }} petani</span>
|
||||
</td>
|
||||
<td class="text-end pe-0">
|
||||
<span>{{ number_format(($skorSummary->where('segmentasi', 'Pemula')->sum('total') / $petaniSummary->sum('total')) * 100, 2) }}%</span>
|
||||
</td>
|
||||
<td class="text-end pe-0">{{ number_format($total) }}</td>
|
||||
<td class="text-end pe-0">{{ number_format($persen, 2) }}%</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -163,45 +141,175 @@
|
||||
<div class="card h-100">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<div class="card-title mb-0">
|
||||
<h5 class="m-0 me-2">Segmentasi Petani Komoditas</h5>
|
||||
<p class="card-subtitle">berdasarkan mayoritas segmentasi</p>
|
||||
<h5 class="m-0 me-2">Segmentasi Petani</h5>
|
||||
<p class="card-subtitle">per komoditas</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table class="table card-table table-border-top-0 table-border-bottom-0">
|
||||
<tbody>
|
||||
@foreach ($komoditas as $k)
|
||||
<?php
|
||||
$max = $skorKomoditasSummary->where('komoditas_id', $k->id)
|
||||
->sortByDesc('total')
|
||||
->first();
|
||||
?>
|
||||
<table class="table card-table table-border-top-0 table-border-bottom-0 data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<td class="w-40 ps-0">
|
||||
<h6 class="mb-0 fw-normal text-heading">{{ $k->nama }}</h6>
|
||||
<th></th>
|
||||
@foreach ($komoditas as $k)
|
||||
<th class="text-end pe-0">{{ $k->nama }}</th>
|
||||
@endforeach
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
@foreach ($segments as $segment)
|
||||
<tr>
|
||||
<td>
|
||||
<div class="d-flex justify-content-start align-items-center">
|
||||
<div class="me-2">
|
||||
<i class="icon-base ti {{ $segment['icon'] }} icon-lg text-{{ $segment['color'] }}"></i>
|
||||
</div>
|
||||
<span class="text-{{ $segment['color'] }}">
|
||||
{{ $segment['name'] }}
|
||||
<small class="text-body-secondary">({{ $segment['range'] }})</small>
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="pe-0">
|
||||
@if(isset($max))
|
||||
@if($max->segmentasi == 'Penggerak')
|
||||
<span class="text-success">Petani Penggerak</span>
|
||||
@elseif($max->segmentasi == 'Perintis')
|
||||
<span class="text-info">Petani Perintis</span>
|
||||
@elseif($max->segmentasi == 'Pemerhati')
|
||||
<span class="text-warning">Petani Pemerhati</span>
|
||||
@elseif($max->segmentasi == 'Pemula')
|
||||
<span class="text-danger">Petani Pemula</span>
|
||||
@else
|
||||
<span>Belum Lengkap</span>
|
||||
@endif
|
||||
@else
|
||||
<span>Belum Ada</span>
|
||||
@endif
|
||||
@foreach ($komoditas as $k)
|
||||
@php
|
||||
$jumlah = $skorKomoditasSummary->where('segmen', $segment['name'])->where('komoditas_id', $k->id)->sum('total');
|
||||
@endphp
|
||||
<td class="text-end pe-0">{{ number_format($jumlah) }}</td>
|
||||
@endforeach
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-6 py-4">
|
||||
<div class="col-lg-6 col-sm-12">
|
||||
<div class="card h-100">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<div class="card-title mb-0">
|
||||
<h5 class="m-0 me-2">Segmentasi Multi Dimensi Petani</h5>
|
||||
<p class="card-subtitle">untuk semua komoditas</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table class="table card-table table-border-top-0 table-border-bottom-0 data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th class="text-end pe-0">Indeks</th>
|
||||
<th class="text-end pe-0">D1</th>
|
||||
<th class="text-end pe-0">D2</th>
|
||||
<th class="text-end pe-0">D3</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
|
||||
@foreach ($segments as $segment)
|
||||
@php
|
||||
$skor = $skorMultiDimensi->firstWhere('segmen', $segment['name']);
|
||||
@endphp
|
||||
<tr>
|
||||
<td>
|
||||
<div class="d-flex justify-content-start align-items-center">
|
||||
<div class="me-2">
|
||||
<i class="icon-base ti {{ $segment['icon'] }} icon-lg text-{{ $segment['color'] }}"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h6 class="mb-0 fw-normal text-{{ $segment['color'] }}">
|
||||
{{ $segment['name'] }}
|
||||
<small class="text-body-secondary">({{ $segment['range'] }})</small>
|
||||
</h6>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-end pe-0">
|
||||
<span>{{ isset($max) ? $max->total : 0 }} Petani</span>
|
||||
{{ number_format($skor?->skor_kesiapan_adopsi ?? 0, 2, '.', ',') }}
|
||||
</td>
|
||||
|
||||
<td class="text-end pe-0">
|
||||
{{ number_format($skor?->skor_d2_sistem_praktik_budidaya ?? 0, 2, '.', ',') }}
|
||||
</td>
|
||||
|
||||
<td class="text-end pe-0">
|
||||
{{ number_format($skor?->skor_d4_sosial_ekonomi ?? 0, 2, '.', ',') }}
|
||||
</td>
|
||||
|
||||
<td class="text-end pe-0">
|
||||
{{ number_format($skor?->skor_d6_rantai_nilai_akses_pasar ?? 0, 2, '.', ',') }}
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6 col-sm-12">
|
||||
<div class="card h-100">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<div class="card-title mb-0">
|
||||
<h5 class="m-0 me-2">Segmentasi Multi Komponen Petani</h5>
|
||||
<p class="card-subtitle">untuk semua komoditas</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table class="table card-table table-border-top-0 table-border-bottom-0 data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th class="text-end pe-0">Indeks</th>
|
||||
<th class="text-end pe-0">K1</th>
|
||||
<th class="text-end pe-0">K2</th>
|
||||
<th class="text-end pe-0">K3</th>
|
||||
<th class="text-end pe-0">K4</th>
|
||||
<th class="text-end pe-0">K5</th>
|
||||
<th class="text-end pe-0">K6</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
@foreach ($segments as $segment)
|
||||
@php
|
||||
$skor = $skorMultiKomponen->firstWhere('segmen', $segment['name']);
|
||||
@endphp
|
||||
<tr>
|
||||
<td class="">
|
||||
<div class="d-flex justify-content-start align-items-center">
|
||||
<div class="me-2">
|
||||
<i class="icon-base ti {{ $segment['icon'] }} icon-lg text-{{ $segment['color'] }}"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h6 class="mb-0 fw-normal text-{{ $segment['color'] }}">
|
||||
{{ $segment['name'] }}
|
||||
<small class="text-body-secondary">({{ $segment['range'] }})</small>
|
||||
</h6>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-end pe-0">
|
||||
<span>{{ isset($max) ? number_format(($max->total / $petaniSummary->sum('total')) * 100, 2) : 0 }}%</span>
|
||||
<span>{{ number_format($skor?->skor_kesiapan_adopsi, 2, '.', ',') }}</span>
|
||||
</td>
|
||||
<td class="text-end pe-0">
|
||||
<span>{{ number_format($skor?->skor_komp_1_d4_k3_motivasi_kapasitas_adopsi, 2, '.', ',') }}</span>
|
||||
</td>
|
||||
<td class="text-end pe-0">
|
||||
<span>{{ number_format($skor?->skor_komp_2_d4_k2_ekonomi_rumah_tangga, 2, '.', ',') }}</span>
|
||||
</td>
|
||||
<td class="text-end pe-0">
|
||||
<span>{{ number_format($skor?->skor_komp_3_d2_praktik_budidaya_gap, 2, '.', ',') }}</span>
|
||||
</td>
|
||||
<td class="text-end pe-0">
|
||||
<span>{{ number_format($skor?->skor_komp_4_d4_k4_keterlibatan_kelembagaan, 2, '.', ',') }}</span>
|
||||
</td>
|
||||
<td class="text-end pe-0">
|
||||
<span>{{ number_format($skor?->skor_komp_5_d4_k5_k6_akses_kebebasan_keuangan, 2, '.', ',') }}</span>
|
||||
</td>
|
||||
<td class="text-end pe-0">
|
||||
<span>{{ number_format($skor?->skor_komp_6_d6_k1_k4_k5_rantai_nilai_akses_pasar, 2, '.', ',') }}</span>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
@@ -211,5 +319,372 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-6 py-4">
|
||||
<div class="col-lg-6 col-sm-12">
|
||||
<div class="card h-100">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<div class="card-title mb-0">
|
||||
<h5 class="m-0 me-2">Rata-rata Dimensi Kesiapan Adopsi</h5>
|
||||
<p class="card-subtitle">untuk semua komoditas</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="chart-dimensi"></div>
|
||||
<div class="d-flex flex-wrap gap-3 mt-3 small justify-content-center">
|
||||
<div>
|
||||
<span class="badge bg-label-primary">D1</span> Sistem & Praktik Budidaya
|
||||
</div>
|
||||
<div>
|
||||
<span class="badge bg-label-primary">D2</span> Sosial Ekonomi Rumah Tangga
|
||||
</div>
|
||||
<div>
|
||||
<span class="badge bg-label-primary">D3</span> Rantai Nilai & Akses Pasar
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6 col-sm-12">
|
||||
<div class="card h-100">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<div class="card-title mb-0">
|
||||
<h5 class="m-0 me-2">Rata-rata Komponen Kesiapan Adopsi</h5>
|
||||
<p class="card-subtitle">untuk semua komoditas</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="chart-komponen"></div>
|
||||
<div class="d-flex flex-wrap gap-3 mt-3 small justify-content-center">
|
||||
<div>
|
||||
<span class="badge bg-label-primary">K1</span> Motivasi & Kapasitas Adopsi
|
||||
</div>
|
||||
<div>
|
||||
<span class="badge bg-label-primary">K2</span> Kondisi Ekonomi Rumah Tangga
|
||||
</div>
|
||||
<div>
|
||||
<span class="badge bg-label-primary">K3</span> Praktik Budidaya & GAP
|
||||
</div>
|
||||
<div>
|
||||
<span class="badge bg-label-primary">K4</span> Keterlibatan Kelembagaan
|
||||
</div>
|
||||
<div>
|
||||
<span class="badge bg-label-primary">K5</span> Akses & Kebebasan Keuangan
|
||||
</div>
|
||||
<div>
|
||||
<span class="badge bg-label-primary">K6</span> Rantai Nilai & Akses Pasar
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-6 py-4">
|
||||
<div class="col-lg-12 col-sm-12">
|
||||
<div class="card h-100">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<div class="card-title mb-0">
|
||||
<h5 class="m-0 me-2">Segmentasi Petani</h5>
|
||||
<p class="card-subtitle">untuk semua komoditas</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive text-nowrap">
|
||||
<table class="table card-table table-border-top-0 table-border-bottom-0" id="tabel-petani">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Petani</th>
|
||||
<th>Jenis Kelamin</th>
|
||||
<th>Usia</th>
|
||||
<th>Komoditas Utama</th>
|
||||
<th>Segmentasi</th>
|
||||
<th>Indeks</th>
|
||||
<th>D2</th>
|
||||
<th>D4</th>
|
||||
<th>D6</th>
|
||||
<th>K1</th>
|
||||
<th>K2</th>
|
||||
<th>K3</th>
|
||||
<th>K4</th>
|
||||
<th>K5</th>
|
||||
<th>K6</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach ($petani as $item)
|
||||
<tr>
|
||||
<td><span class="text-heading">{{ $item->nama }}</span></td>
|
||||
<td>{{ $item->jenis_kelamin ? 'Laki-laki' : 'Perempuan' }}</td>
|
||||
<td>{{ $item->umur }} tahun</td>
|
||||
<td>{{ $item->komoditas?->nama }}</td>
|
||||
<td>
|
||||
@if($item->segmen() == 'Penggerak')
|
||||
<span class="badge bg-label-success">Penggerak</span>
|
||||
@elseif($item->segmen() == 'Perintis')
|
||||
<span class="badge bg-label-info">Perintis</span>
|
||||
@elseif($item->segmen() == 'Pemerhati')
|
||||
<span class="badge bg-label-warning">Pemerhati</span>
|
||||
@elseif($item->segmen() == 'Pemula')
|
||||
<span class="badge bg-label-danger">Pemula</span>
|
||||
@endif
|
||||
</td>
|
||||
<td>{{ number_format($item->skor?->skor_kesiapan_adopsi, 2, '.', ',') }}</td>
|
||||
<td>{{ number_format($item->skor?->skor_d2_sistem_praktik_budidaya, 2, '.', ',') }}</td>
|
||||
<td>{{ number_format($item->skor?->skor_d4_sosial_ekonomi, 2, '.', ',') }}</td>
|
||||
<td>{{ number_format($item->skor?->skor_d6_rantai_nilai_akses_pasar, 2, '.', ',') }}</td>
|
||||
<td>{{ number_format($item->skor?->skor_komp_1_d4_k3_motivasi_kapasitas_adopsi, 2, '.', ',') }}</td>
|
||||
<td>{{ number_format($item->skor?->skor_komp_2_d4_k2_ekonomi_rumah_tangga, 2, '.', ',') }}</td>
|
||||
<td>{{ number_format($item->skor?->skor_komp_3_d2_praktik_budidaya_gap, 2, '.', ',') }}</td>
|
||||
<td>{{ number_format($item->skor?->skor_komp_4_d4_k4_keterlibatan_kelembagaan, 2, '.', ',') }}</td>
|
||||
<td>{{ number_format($item->skor?->skor_komp_5_d4_k5_k6_akses_kebebasan_keuangan, 2, '.', ',') }}</td>
|
||||
<td>{{ number_format($item->skor?->skor_komp_6_d6_k1_k4_k5_rantai_nilai_akses_pasar, 2, '.', ',') }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
<script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
|
||||
$('.data-table').DataTable({
|
||||
"paging": false,
|
||||
"lengthChange": true,
|
||||
"searching": false,
|
||||
"ordering": false,
|
||||
"info": false,
|
||||
"autoWidth": false,
|
||||
"responsive": false,
|
||||
"scrollX": true
|
||||
});
|
||||
|
||||
$("#tabel-petani").DataTable({
|
||||
"paging": true,
|
||||
"lengthChange": true,
|
||||
"searching": true,
|
||||
"ordering": false,
|
||||
"info": true,
|
||||
"autoWidth": false,
|
||||
"responsive": false,
|
||||
"scrollX": true
|
||||
})
|
||||
|
||||
|
||||
let cardColor, labelColor, fontFamily, headingColor, shadeColor, legendColor, borderColor, barBgColor;
|
||||
if (isDarkStyle) {
|
||||
barBgColor = '#3d4157';
|
||||
shadeColor = 'dark';
|
||||
} else {
|
||||
barBgColor = '#efeef0';
|
||||
shadeColor = '';
|
||||
}
|
||||
cardColor = config.colors.cardColor;
|
||||
labelColor = config.colors.textMuted;
|
||||
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 = {
|
||||
chart: {
|
||||
height: 340,
|
||||
type: 'radar',
|
||||
toolbar: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: "Rata-rata Dimensi Kesiapan Adopsi",
|
||||
data: [{{ round($skorDimensi->skor_d2_sistem_praktik_budidaya, 2) }},
|
||||
{{ round($skorDimensi->skor_d4_sosial_ekonomi, 2) }},
|
||||
{{ round($skorDimensi->skor_d6_rantai_nilai_akses_pasar, 2) }}]
|
||||
}
|
||||
],
|
||||
labels: ['Sistem & Praktik Budidaya', 'Sosial Ekonomi Rumah Tangga', 'Rantai Nilai & Akses Pasar'],
|
||||
plotOptions: {
|
||||
radar: {
|
||||
polygons: {
|
||||
strokeColors: borderColor,
|
||||
connectorColors: borderColor
|
||||
}
|
||||
}
|
||||
},
|
||||
stroke: {
|
||||
width: 2
|
||||
},
|
||||
legend: {
|
||||
show: true,
|
||||
fontSize: '13px',
|
||||
position: 'bottom',
|
||||
labels: {
|
||||
colors: legendColor,
|
||||
useSeriesColors: false
|
||||
},
|
||||
markers: {
|
||||
height: 12,
|
||||
width: 12
|
||||
},
|
||||
itemMargin: {
|
||||
horizontal: 10
|
||||
},
|
||||
onItemHover: {
|
||||
highlightDataSeries: false
|
||||
}
|
||||
},
|
||||
colors: [pastelSemantic.primary || '#9D8EF0', pastelSemantic.info || '#5FC7D6'],
|
||||
fill: {
|
||||
opacity: 0.85
|
||||
},
|
||||
markers: {
|
||||
show: true,
|
||||
width: 1,
|
||||
},
|
||||
grid: {
|
||||
show: false,
|
||||
padding: {
|
||||
top: -25,
|
||||
bottom: -5
|
||||
}
|
||||
},
|
||||
xaxis: {
|
||||
categories: ['D1', 'D2', 'D3'],
|
||||
labels: {
|
||||
show: true,
|
||||
style: {
|
||||
colors: [labelColor, labelColor, labelColor, labelColor, labelColor, labelColor],
|
||||
fontSize: '13px',
|
||||
fontFamily: fontFamily
|
||||
}
|
||||
}
|
||||
},
|
||||
yaxis: {
|
||||
show: true,
|
||||
min: 0,
|
||||
max: 5,
|
||||
tickAmount: 5
|
||||
},
|
||||
responsive: [
|
||||
{
|
||||
breakpoint: 769,
|
||||
options: {
|
||||
chart: {
|
||||
height: 400
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
radarDimensi.render();
|
||||
|
||||
|
||||
var radarKomponen = new ApexCharts($("#chart-komponen")[0],
|
||||
options = {
|
||||
chart: {
|
||||
height: 340,
|
||||
type: 'radar',
|
||||
toolbar: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: "Rata-rata Komponen Kesiapan Adopsi",
|
||||
data: [
|
||||
{{ round($skorKomponen->skor_komp_1_d4_k3_motivasi_kapasitas_adopsi, 2) }},
|
||||
{{ round($skorKomponen->skor_komp_2_d4_k2_ekonomi_rumah_tangga, 2) }},
|
||||
{{ round($skorKomponen->skor_komp_3_d2_praktik_budidaya_gap, 2) }},
|
||||
{{ round($skorKomponen->skor_komp_4_d4_k4_keterlibatan_kelembagaan, 2) }},
|
||||
{{ round($skorKomponen->skor_komp_5_d4_k5_k6_akses_kebebasan_keuangan, 2) }},
|
||||
{{ round($skorKomponen->skor_komp_6_d6_k1_k4_k5_rantai_nilai_akses_pasar, 2) }}
|
||||
]
|
||||
}
|
||||
],
|
||||
lotOptions: {
|
||||
radar: {
|
||||
polygons: {
|
||||
strokeColors: borderColor,
|
||||
connectorColors: borderColor
|
||||
}
|
||||
}
|
||||
},
|
||||
stroke: {
|
||||
width: 2
|
||||
},
|
||||
legend: {
|
||||
show: true,
|
||||
fontSize: '13px',
|
||||
position: 'bottom',
|
||||
labels: {
|
||||
colors: legendColor,
|
||||
useSeriesColors: false
|
||||
},
|
||||
markers: {
|
||||
height: 12,
|
||||
width: 12
|
||||
},
|
||||
itemMargin: {
|
||||
horizontal: 10
|
||||
},
|
||||
onItemHover: {
|
||||
highlightDataSeries: false
|
||||
}
|
||||
},
|
||||
colors: [pastelSemantic.primary || '#9D8EF0', pastelSemantic.info || '#5FC7D6'],
|
||||
fill: {
|
||||
opacity: 0.85
|
||||
},
|
||||
markers: {
|
||||
show: true,
|
||||
width: 1,
|
||||
},
|
||||
grid: {
|
||||
show: false,
|
||||
padding: {
|
||||
top: -25,
|
||||
bottom: -5
|
||||
}
|
||||
},
|
||||
xaxis: {
|
||||
categories: ['K1', 'K2', 'K3', 'K4', 'K5', 'K6'],
|
||||
labels: {
|
||||
show: true,
|
||||
style: {
|
||||
colors: [labelColor, labelColor, labelColor, labelColor, labelColor, labelColor],
|
||||
fontSize: '13px',
|
||||
fontFamily: fontFamily
|
||||
}
|
||||
}
|
||||
},
|
||||
yaxis: {
|
||||
show: true,
|
||||
min: 0,
|
||||
max: 5,
|
||||
tickAmount: 5
|
||||
},
|
||||
responsive: [
|
||||
{
|
||||
breakpoint: 769,
|
||||
options: {
|
||||
chart: {
|
||||
height: 400
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
radarKomponen.render();
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user