Compare commits
275
Commits
d509a79576
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a3ec431d49 | ||
|
|
fd93e0d265 | ||
|
|
8ef4ae1f15 | ||
|
|
89c5a1371a | ||
|
|
41da3a71ee | ||
|
|
b5b046ed86 | ||
|
|
aa91c3fe3f | ||
|
|
53913bebe9 | ||
|
|
f455f8b878 | ||
|
|
f1112e3b68 | ||
|
|
c1370a26ca | ||
|
|
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 | ||
|
|
4a560ebc4e | ||
|
|
d4eb27e583 | ||
|
|
d1c0f90afd | ||
|
|
a1dadd1b1b | ||
|
|
2c28bb06db | ||
|
|
96a53c8754 | ||
|
|
fcc5d7dcc5 | ||
|
|
e383aacbeb | ||
|
|
5b776b2ae8 | ||
|
|
c4e3e460b1 | ||
|
|
a7dc1dc58b | ||
|
|
d01c737364 | ||
|
|
8d50757b51 | ||
|
|
f146cb2608 | ||
|
|
095d84ec7f | ||
|
|
f259c6728a | ||
|
|
a241a62136 | ||
|
|
4755c8a482 | ||
|
|
7672877299 | ||
|
|
92e8efb247 | ||
|
|
599269410b | ||
|
|
c5b5721b31 | ||
|
|
2bf2a68a3a | ||
|
|
b445392e9f | ||
|
|
bcc15d5240 | ||
|
|
d33bc8a42b | ||
|
|
df5bfeb67f | ||
|
|
b0e1f3f131 | ||
|
|
a19871366a | ||
|
|
b4b851c7a9 | ||
|
|
e6f6257434 | ||
|
|
469e57ba28 | ||
|
|
d06da22ae5 | ||
|
|
350df6adcf | ||
|
|
1788efaed5 | ||
|
|
d49bd4953c | ||
|
|
26f6e83328 | ||
|
|
79f6426125 | ||
|
|
9557d1ec64 | ||
|
|
7bab43df71 | ||
|
|
316b256ae0 | ||
|
|
bfabab96c5 | ||
|
|
bd9b0873a3 | ||
|
|
3464a802e2 | ||
|
|
6c95c462cb | ||
|
|
626fdba065 | ||
|
|
2152079b26 | ||
|
|
6559a97f43 | ||
|
|
05c2e53378 | ||
|
|
3df381a66c | ||
|
|
6fac76127d | ||
|
|
a1d9b34d93 | ||
|
|
c01ba6a906 | ||
|
|
4976948bc6 | ||
|
|
734c3fba65 | ||
|
|
457288b325 | ||
|
|
9220cf23c2 | ||
|
|
884b911767 | ||
|
|
e1fde05ba0 | ||
|
|
74572fe586 | ||
|
|
2eb40f8a1a | ||
|
|
ff3f930939 | ||
|
|
b3d805fe98 | ||
|
|
e2c778a934 | ||
|
|
34865b34be | ||
|
|
11d836713a | ||
|
|
f448257980 | ||
|
|
f9f507a115 | ||
|
|
6ddf978ba7 | ||
|
|
1fd47b517b | ||
|
|
13c71d2612 | ||
|
|
d0e41dc1d2 | ||
|
|
21679486ea | ||
|
|
7cd32b1116 | ||
|
|
e9e598616e | ||
|
|
bd40cd9357 | ||
|
|
eea9f4dec8 | ||
|
|
27e084bc9e | ||
|
|
3b1a0f52dd | ||
|
|
5cee65f8df | ||
|
|
5c1fe22fa0 | ||
|
|
8886629e02 | ||
|
|
e1979b51dd | ||
|
|
55598fbf1d | ||
|
|
90f13438db | ||
|
|
c454a0b453 | ||
|
|
a4a81447ab | ||
|
|
c4ff20facd | ||
|
|
8710176b2c | ||
|
|
10df07b473 | ||
|
|
cc6a732de7 | ||
|
|
839e9aa572 | ||
|
|
280f76510e | ||
|
|
02890ae9ae | ||
|
|
1517dd38cf | ||
|
|
871b015ce2 | ||
|
|
7d40bcfb53 | ||
|
|
aecca02269 | ||
|
|
d5f1a5f625 | ||
|
|
cdbfff4424 | ||
|
|
e640cba74f | ||
|
|
18bdb3abca | ||
|
|
8cba4118bf | ||
|
|
f8304f8f84 | ||
|
|
8ead70787c | ||
|
|
d7e2cd7692 | ||
|
|
9d3b64a54d | ||
|
|
35b50cba36 | ||
|
|
b6c94ddb07 | ||
|
|
bf1fe7fcfe | ||
|
|
f270c1f0b6 | ||
|
|
482c3fc474 |
@@ -63,3 +63,9 @@ AWS_BUCKET=
|
|||||||
AWS_USE_PATH_STYLE_ENDPOINT=false
|
AWS_USE_PATH_STYLE_ENDPOINT=false
|
||||||
|
|
||||||
VITE_APP_NAME="${APP_NAME}"
|
VITE_APP_NAME="${APP_NAME}"
|
||||||
|
|
||||||
|
# Optional CA bundle path for Yahoo Finance when PHP has no system CA configured.
|
||||||
|
YAHOO_FINANCE_CA_BUNDLE=
|
||||||
|
|
||||||
|
|
||||||
|
ALPHA_VANTAGE_API_KEY=
|
||||||
|
|||||||
+714
@@ -0,0 +1,714 @@
|
|||||||
|
# AISTANDAR — Standar Pengembangan Aplikasi database-pertani-web
|
||||||
|
|
||||||
|
Dokumen ini adalah **aturan wajib** bagi AI (dan developer) yang bekerja di repo ini.
|
||||||
|
Baca seluruhnya sebelum membuat, mengubah, atau merefaktor kode.
|
||||||
|
|
||||||
|
## 0. Cara memakai dokumen ini
|
||||||
|
|
||||||
|
- Aturan di sini **mengalahkan** kebiasaan umum atau contoh dari luar. Jika ada konflik antara kode lama dan dokumen ini, ikuti dokumen ini **untuk kode baru**, dan ikuti Bagian 13 (Prosedur Refaktor) untuk kode lama.
|
||||||
|
- Jika sesuatu **tidak diatur** di sini: lihat dulu bagaimana modul yang sudah sesuai standar melakukannya, lalu tiru polanya. Jika tetap tidak jelas, **tanya**, jangan mengarang pola baru.
|
||||||
|
- **Jangan** menambah package/library baru, mengubah struktur database yang sudah ada, atau mengubah kontrak API tanpa izin eksplisit.
|
||||||
|
- Bahasa: teks yang tampil ke pengguna (label, pesan, validasi) memakai **Bahasa Indonesia**. Nama domain (petani, lahan, komoditas) tetap Bahasa Indonesia; kata kerja teknis Laravel (`index`, `store`, `show`) tetap bahasa Inggris.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Stack
|
||||||
|
|
||||||
|
| Bagian | Teknologi |
|
||||||
|
|---|---|
|
||||||
|
| Framework | Laravel 12 |
|
||||||
|
| Frontend web | Blade (tanpa Livewire/Inertia) |
|
||||||
|
| UI template | Vuexy (Bootstrap 5), ikon Tabler |
|
||||||
|
| Grafik | ApexCharts |
|
||||||
|
| Peta | Leaflet |
|
||||||
|
| Auth web | Session |
|
||||||
|
| Auth API | Laravel Sanctum (token) |
|
||||||
|
| Dokumentasi API | dedoc/scramble |
|
||||||
|
| Role | `app/Enums/Role.php`: `super_admin`, `admin` (admin program), `koordinator` (per komoditas), `enumerator` (penginput data petani), `fasilitator` (pemateri workshop), `petani`, `offtaker` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Struktur folder & penamaan
|
||||||
|
|
||||||
|
### 2.1 Pengelompokan per domain
|
||||||
|
|
||||||
|
Controller, Model, Request, dan View **dikelompokkan per domain yang sama**:
|
||||||
|
|
||||||
|
```
|
||||||
|
Domain yang ada: Dashboard, Laporan, Produktivitas, Ekosistem, Program, Master,
|
||||||
|
Bumdes, Koperasi, Konfigurasi
|
||||||
|
```
|
||||||
|
|
||||||
|
| Lapisan | Lokasi |
|
||||||
|
|---|---|
|
||||||
|
| Controller web | `app/Http/Controllers/Web/{Domain}/{Model}Controller.php` |
|
||||||
|
| Controller API | `app/Http/Controllers/Api/{Domain}/{Model}Controller.php` |
|
||||||
|
| Form Request | `app/Http/Requests/{Domain}/Store{Model}Request.php`, `Update{Model}Request.php` |
|
||||||
|
| Model | `app/Models/{DomainModel}/{Model}.php` (ikuti folder model yang ada: `Pertanian/`, `Wilayah/`, `Program/`, `Master/`, dst.) |
|
||||||
|
| Enum | `app/Enums/` |
|
||||||
|
| Service / Query | `app/Services/{Domain}/` |
|
||||||
|
| View | `resources/views/{domain}/{modul}/` (huruf kecil, kebab-case) |
|
||||||
|
|
||||||
|
Controller baru **tidak boleh** ditaruh di root `app/Http/Controllers/`. Controller root yang sudah ada (`ProgramController`, `ProgramKomoditasController`, `ProgramLokasiController`) dipindahkan ke domain `Program/` saat direfaktor.
|
||||||
|
|
||||||
|
### 2.2 Konvensi nama
|
||||||
|
|
||||||
|
| Objek | Aturan | Contoh |
|
||||||
|
|---|---|---|
|
||||||
|
| Model | Singular, PascalCase | `Petani`, `JenisKomoditas` |
|
||||||
|
| Tabel | snake_case, **selalu ditulis eksplisit** di model | `protected $table = 'petani';` |
|
||||||
|
| Controller | `{Model}Controller` | `PetaniController` |
|
||||||
|
| Route name | `{modul}.{aksi}`, kebab-case | `jenis-komoditas.index` |
|
||||||
|
| URL | kebab-case | `/jenis-komoditas` |
|
||||||
|
| View | `{domain}/{modul}/{aksi}.blade.php` | `master/provinsi/index.blade.php` |
|
||||||
|
| Method tambahan | camelCase | `byProvinsi()`, `list()` |
|
||||||
|
|
||||||
|
> Karena nama domain berbahasa Indonesia, pluralisasi otomatis Laravel salah (`petanis`). **Wajib** set `$table` di setiap model, dan set parameter route bila perlu (`->parameters(['jenis-komoditas' => 'jenisKomoditas'])`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Controller
|
||||||
|
|
||||||
|
> **Cakupan:** Bagian 3 berlaku untuk **modul CRUD** (pengelolaan data: tambah, ubah, hapus).
|
||||||
|
> Controller **Dashboard** dan **Laporan/Reporting** tidak terikat aturan 7 method dan mengikuti aturan yang lebih fleksibel di **Bagian 11**.
|
||||||
|
|
||||||
|
### 3.1 Tujuh method resmi
|
||||||
|
|
||||||
|
Untuk modul CRUD, gunakan **hanya** nama method resource Laravel:
|
||||||
|
|
||||||
|
| Method | HTTP | URL | Route name | Fungsi |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| `index` | GET | `/items` | `items.index` | Daftar data |
|
||||||
|
| `create` | GET | `/items/create` | `items.create` | Form tambah |
|
||||||
|
| `store` | POST | `/items` | `items.store` | Simpan baru |
|
||||||
|
| `show` | GET | `/items/{item}` | `items.show` | Detail |
|
||||||
|
| `edit` | GET | `/items/{item}/edit` | `items.edit` | Form ubah |
|
||||||
|
| `update` | PUT/PATCH | `/items/{item}` | `items.update` | Simpan perubahan |
|
||||||
|
| `destroy` | DELETE | `/items/{item}` | `items.destroy` | Hapus (soft delete) |
|
||||||
|
|
||||||
|
**Dilarang** memakai nama `add`, `view`, `save`, `delete`, `remove`, `detail`, atau sejenisnya untuk aksi CRUD. (Larangan ini tidak berlaku untuk Dashboard dan Laporan, lihat Bagian 11.)
|
||||||
|
|
||||||
|
### 3.2 Prinsip controller tipis
|
||||||
|
|
||||||
|
- Validasi **selalu** di Form Request, tidak pernah `$request->validate()` di controller.
|
||||||
|
- Controller hanya: terima request → panggil model/service → kembalikan respons.
|
||||||
|
- Logika bisnis yang lebih dari ±15 baris, dipakai di lebih dari satu tempat, atau melibatkan banyak tabel → pindahkan ke `app/Services/{Domain}/`.
|
||||||
|
- Operasi yang menulis ke lebih dari satu tabel → bungkus dengan `DB::transaction()`.
|
||||||
|
- Pakai route model binding (`Petani $petani`), bukan `find($id)`.
|
||||||
|
|
||||||
|
### 3.3 Tiga pola resmi
|
||||||
|
|
||||||
|
Pilih **satu** pola per modul berdasarkan tabel ini:
|
||||||
|
|
||||||
|
| Pola | Kapan dipakai | Contoh |
|
||||||
|
|---|---|---|
|
||||||
|
| **A. Offcanvas (AJAX)** | Data referensi/sederhana: sedikit field, tanpa halaman detail yang kaya | Provinsi, Kabupaten, Komoditas, Varietas, Adat |
|
||||||
|
| **B. Resource halaman** | Entitas utama: banyak field, ada relasi, perlu halaman detail | Lahan, Produksi, Workshop, BUMDes, Koperasi |
|
||||||
|
| **C. Detail multi-tab** | Entitas induk dengan banyak bagian data yang diisi bertahap | Petani, Survey Kelompok Tani |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Pola A — Offcanvas (AJAX)
|
||||||
|
|
||||||
|
- `index` mengembalikan **view**; `store`, `show`, `update`, `destroy` mengembalikan **JSON**.
|
||||||
|
- Tidak ada halaman `create`/`edit`; form ada di offcanvas di `index.blade.php`.
|
||||||
|
- `show` dipakai JS untuk mengisi form edit di offcanvas.
|
||||||
|
|
||||||
|
```php
|
||||||
|
// routes/web.php
|
||||||
|
// Route tambahan WAJIB didaftarkan SEBELUM Route::resource agar tidak tertangkap {provinsi}
|
||||||
|
Route::get('provinsi/list', [ProvinsiController::class, 'list'])->name('provinsi.list');
|
||||||
|
Route::resource('provinsi', ProvinsiController::class)->except(['create', 'edit']);
|
||||||
|
```
|
||||||
|
|
||||||
|
```php
|
||||||
|
namespace App\Http\Controllers\Web\Master;
|
||||||
|
|
||||||
|
class ProvinsiController extends Controller
|
||||||
|
{
|
||||||
|
public function index(Request $request)
|
||||||
|
{
|
||||||
|
$provinsi = Provinsi::query()
|
||||||
|
->when($request->search, fn ($q, $s) => $q->where('nama', 'like', "%{$s}%"))
|
||||||
|
->latest()
|
||||||
|
->paginate(15)
|
||||||
|
->withQueryString();
|
||||||
|
|
||||||
|
return view('master.provinsi.index', compact('provinsi'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function store(StoreProvinsiRequest $request)
|
||||||
|
{
|
||||||
|
$provinsi = Provinsi::create($request->validated());
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'message' => 'Provinsi berhasil ditambahkan.',
|
||||||
|
'data' => $provinsi,
|
||||||
|
], 201);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function show(Provinsi $provinsi)
|
||||||
|
{
|
||||||
|
return response()->json(['success' => true, 'data' => $provinsi]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(UpdateProvinsiRequest $request, Provinsi $provinsi)
|
||||||
|
{
|
||||||
|
$provinsi->update($request->validated());
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'message' => 'Provinsi berhasil diperbarui.',
|
||||||
|
'data' => $provinsi,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function destroy(Provinsi $provinsi)
|
||||||
|
{
|
||||||
|
DB::transaction(fn () => $provinsi->delete());
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'message' => 'Provinsi berhasil dihapus.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Dropdown berantai** (provinsi → kabupaten → kecamatan → desa): method `by{Induk}` di controller anak, mengembalikan JSON `[{id, nama}]`.
|
||||||
|
|
||||||
|
```php
|
||||||
|
Route::get('kabupaten/by-provinsi/{provinsi}', [KabupatenController::class, 'byProvinsi'])
|
||||||
|
->name('kabupaten.by-provinsi');
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Pola B — Resource halaman
|
||||||
|
|
||||||
|
- Semua 7 method; view `index`, `create`, `edit`, `show` + partial `_form`.
|
||||||
|
- Setelah `store`/`update`/`destroy` → **redirect** dengan flash message.
|
||||||
|
|
||||||
|
```php
|
||||||
|
Route::resource('lahan', LahanController::class);
|
||||||
|
```
|
||||||
|
|
||||||
|
```php
|
||||||
|
namespace App\Http\Controllers\Web\Produktivitas;
|
||||||
|
|
||||||
|
class LahanController extends Controller
|
||||||
|
{
|
||||||
|
public function index(Request $request)
|
||||||
|
{
|
||||||
|
$lahan = Lahan::query()
|
||||||
|
->with(['petani', 'desa'])
|
||||||
|
->when($request->search, fn ($q, $s) => $q->where('nama', 'like', "%{$s}%"))
|
||||||
|
->latest()
|
||||||
|
->paginate(15)
|
||||||
|
->withQueryString();
|
||||||
|
|
||||||
|
return view('produktivitas.lahan.index', compact('lahan'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create()
|
||||||
|
{
|
||||||
|
return view('produktivitas.lahan.create', ['lahan' => new Lahan()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function store(StoreLahanRequest $request)
|
||||||
|
{
|
||||||
|
$lahan = Lahan::create($request->validated());
|
||||||
|
|
||||||
|
return redirect()->route('lahan.show', $lahan)
|
||||||
|
->with('success', 'Lahan berhasil ditambahkan.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function show(Lahan $lahan)
|
||||||
|
{
|
||||||
|
$lahan->load(['petani', 'tanaman']);
|
||||||
|
|
||||||
|
return view('produktivitas.lahan.show', compact('lahan'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function edit(Lahan $lahan)
|
||||||
|
{
|
||||||
|
return view('produktivitas.lahan.edit', compact('lahan'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(UpdateLahanRequest $request, Lahan $lahan)
|
||||||
|
{
|
||||||
|
$lahan->update($request->validated());
|
||||||
|
|
||||||
|
return redirect()->route('lahan.show', $lahan)
|
||||||
|
->with('success', 'Lahan berhasil diperbarui.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function destroy(Lahan $lahan)
|
||||||
|
{
|
||||||
|
DB::transaction(fn () => $lahan->delete());
|
||||||
|
|
||||||
|
return redirect()->route('lahan.index')
|
||||||
|
->with('success', 'Lahan berhasil dihapus.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Pola C — Detail multi-tab
|
||||||
|
|
||||||
|
- Controller induk (mis. `PetaniController`) memakai Pola B.
|
||||||
|
- Setiap tab punya **controller sendiri** (`PetaniLahanController`, `PraktikBudidayaController`, …) yang tetap memakai nama method resmi.
|
||||||
|
- Tab dirangkai sebagai **nested shallow resource** di bawah induk.
|
||||||
|
- View: `header.blade.php` berisi kartu profil + navigasi tab; setiap tab punya subfolder.
|
||||||
|
- Tab selain Profil **terkunci** sampai profil induk lengkap. Logika penguncian ditaruh di satu tempat (method di model induk, mis. `$petani->profilLengkap()`), bukan diulang di tiap view.
|
||||||
|
|
||||||
|
```
|
||||||
|
resources/views/produktivitas/petani/
|
||||||
|
├── index.blade.php
|
||||||
|
├── create.blade.php
|
||||||
|
├── edit.blade.php
|
||||||
|
├── show.blade.php ← tab Profil
|
||||||
|
├── header.blade.php ← kartu profil + navigasi tab
|
||||||
|
├── _form.blade.php
|
||||||
|
├── lahan-tanaman/
|
||||||
|
│ ├── index.blade.php
|
||||||
|
│ └── ...
|
||||||
|
├── praktik-budidaya/
|
||||||
|
└── ...
|
||||||
|
```
|
||||||
|
|
||||||
|
```php
|
||||||
|
Route::resource('petani', PetaniController::class);
|
||||||
|
Route::resource('petani.praktik-budidaya', PraktikBudidayaController::class)->shallow();
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.4 Batas resource
|
||||||
|
|
||||||
|
- Jika modul tidak butuh semua aksi, batasi dengan `->only([...])` / `->except([...])`. Jangan biarkan route yang tidak punya method terdaftar.
|
||||||
|
- Method di luar 7 aksi resmi hanya boleh untuk: dropdown/list (`list`, `by{Induk}`), aksi status yang jelas (`verify`, `approve`), dan export (`export`). Daftarkan sebelum `Route::resource`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Validasi
|
||||||
|
|
||||||
|
- Setiap `store` dan `update` memakai Form Request: `Store{Model}Request`, `Update{Model}Request`.
|
||||||
|
- Pesan dan atribut dalam Bahasa Indonesia (`messages()` / `attributes()`, atau file `lang/id/validation.php`).
|
||||||
|
- Form Request yang sama boleh dipakai controller Web dan API.
|
||||||
|
- Untuk Pola A, Form Request otomatis mengembalikan JSON 422 karena request AJAX mengirim header `Accept: application/json`. **Jangan** menangkap error validasi secara manual.
|
||||||
|
- **Unique wajib mengabaikan data yang sudah di-soft delete** (lihat 5.3).
|
||||||
|
|
||||||
|
```php
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'kode' => [
|
||||||
|
'required', 'string', 'max:10',
|
||||||
|
Rule::unique('provinsi', 'kode')
|
||||||
|
->ignore($this->route('provinsi'))
|
||||||
|
->withoutTrashed(),
|
||||||
|
],
|
||||||
|
'nama' => ['required', 'string', 'max:100'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Model, migration, dan soft delete
|
||||||
|
|
||||||
|
### 5.1 Model
|
||||||
|
|
||||||
|
```php
|
||||||
|
class Lahan extends Model
|
||||||
|
{
|
||||||
|
use SoftDeletes;
|
||||||
|
|
||||||
|
protected $table = 'lahan';
|
||||||
|
|
||||||
|
protected $fillable = ['petani_id', 'desa_id', 'nama', 'luas'];
|
||||||
|
|
||||||
|
protected function casts(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'luas' => 'decimal:2',
|
||||||
|
'status_milik' => StatusKepemilikan::class, // Enum
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- Pakai `$fillable`, bukan `$guarded = []`.
|
||||||
|
- Pilihan tetap (jawaban survei, status) disimpan sebagai **PHP Enum** di `app/Enums/` dan di-cast di model. Jangan menulis daftar pilihan langsung di Blade.
|
||||||
|
|
||||||
|
### 5.2 Migration
|
||||||
|
|
||||||
|
Setiap tabel baru **wajib** memiliki:
|
||||||
|
|
||||||
|
```php
|
||||||
|
$table->id();
|
||||||
|
// ... kolom
|
||||||
|
$table->foreignId('created_by')->nullable()->constrained('users'); // ikuti kolom audit yang sudah ada
|
||||||
|
$table->foreignId('updated_by')->nullable()->constrained('users');
|
||||||
|
$table->timestamps();
|
||||||
|
$table->softDeletes();
|
||||||
|
$table->foreignId('deleted_by')->nullable()->constrained('users');
|
||||||
|
```
|
||||||
|
|
||||||
|
- Jangan mengubah migration yang sudah dijalankan di production; buat migration baru.
|
||||||
|
- **Jangan** memakai unique index di database untuk kolom pada tabel ber-soft delete (data terhapus akan menghalangi input ulang). Keunikan dijaga di Form Request.
|
||||||
|
|
||||||
|
### 5.3 Soft delete (aturan utama)
|
||||||
|
|
||||||
|
1. **Semua penghapusan adalah soft delete.** `forceDelete()` dilarang di kode aplikasi.
|
||||||
|
2. **`deleted_by` wajib terisi** dengan user yang menghapus. Gunakan mekanisme yang sudah ada di project (trait/observer). Jika belum ada, gunakan trait berikut dan pasang di semua model:
|
||||||
|
|
||||||
|
```php
|
||||||
|
namespace App\Models\Concerns;
|
||||||
|
|
||||||
|
trait HasDeletedBy
|
||||||
|
{
|
||||||
|
protected static function bootHasDeletedBy(): void
|
||||||
|
{
|
||||||
|
static::deleting(function ($model) {
|
||||||
|
if (! $model->isForceDeleting()) {
|
||||||
|
$model->deleted_by = auth()->id();
|
||||||
|
$model->saveQuietly();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Hapus berantai (cascade soft delete).** Jika induk dihapus, **semua data anaknya ikut di-soft delete**, termasuk cucu dan seterusnya. Tulis di model induk:
|
||||||
|
|
||||||
|
```php
|
||||||
|
protected static function booted(): void
|
||||||
|
{
|
||||||
|
static::deleting(function (Petani $petani) {
|
||||||
|
if ($petani->isForceDeleting()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// ->each->delete() memicu event per anak, sehingga deleted_by
|
||||||
|
// terisi dan cascade berlanjut ke cucu
|
||||||
|
$petani->lahan()->each->delete();
|
||||||
|
$petani->praktikBudidaya()->each->delete();
|
||||||
|
// ... semua relasi hasMany/hasOne milik petani
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Jangan** memakai `$petani->lahan()->delete()` (query massal), karena tidak memicu event, sehingga `deleted_by` kosong dan cascade terputus.
|
||||||
|
- Setiap kali menambah relasi anak baru, **tambahkan juga** ke blok `deleting` induknya.
|
||||||
|
- Pemanggilan `delete()` di controller selalu dibungkus `DB::transaction()`.
|
||||||
|
- Relasi `belongsTo` ke data master (Provinsi, Komoditas, dll.) **bukan** anak dan tidak ikut terhapus.
|
||||||
|
4. **Restore** belum disediakan di aplikasi; dilakukan langsung di database oleh admin. Jangan membuat halaman sampah/restore tanpa diminta.
|
||||||
|
|
||||||
|
### 5.4 Global scope
|
||||||
|
|
||||||
|
- `KomoditasProgramScope` otomatis membatasi data ke komoditas milik program aktif (`SystemProgram`). Jika data "tidak muncul", periksa scope ini dulu.
|
||||||
|
- Melewati scope hanya dengan `withoutGlobalScope(KomoditasProgramScope::class)` secara eksplisit, dengan komentar alasannya.
|
||||||
|
- Jangan menambah global scope baru tanpa izin.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Routing
|
||||||
|
|
||||||
|
### 6.1 Route CRUD wajib memakai `Route::resource`
|
||||||
|
|
||||||
|
Semua modul CRUD (Pola A, B, C) **wajib** didaftarkan dengan `Route::resource`. **Dilarang** menulis route CRUD satu per satu secara manual.
|
||||||
|
|
||||||
|
```php
|
||||||
|
// ❌ SALAH — route CRUD manual
|
||||||
|
Route::get('provinsi', [ProvinsiController::class, 'index'])->name('provinsi.index');
|
||||||
|
Route::post('provinsi/store', [ProvinsiController::class, 'store'])->name('provinsi.store');
|
||||||
|
Route::get('provinsi/view/{id}', [ProvinsiController::class, 'view'])->name('provinsi.view');
|
||||||
|
Route::post('provinsi/update/{id}', [ProvinsiController::class, 'update'])->name('provinsi.update');
|
||||||
|
Route::get('provinsi/delete/{id}', [ProvinsiController::class, 'destroy'])->name('provinsi.destroy');
|
||||||
|
|
||||||
|
// ✅ BENAR
|
||||||
|
Route::resource('provinsi', ProvinsiController::class)->except(['create', 'edit']);
|
||||||
|
```
|
||||||
|
|
||||||
|
Aturan turunannya:
|
||||||
|
|
||||||
|
| Kebutuhan | Cara |
|
||||||
|
|---|---|
|
||||||
|
| Tidak semua aksi dipakai | `->only([...])` atau `->except([...])` |
|
||||||
|
| Pola A (offcanvas) | `->except(['create', 'edit'])` |
|
||||||
|
| Pola B (resource halaman) | `Route::resource(...)` penuh |
|
||||||
|
| Pola C (multi-tab) | Induk `Route::resource`, tab `Route::resource('induk.tab', ...)->shallow()` |
|
||||||
|
| Beberapa modul sekaligus | `Route::resources([...])` boleh dipakai dalam satu grup domain |
|
||||||
|
| Nama parameter tidak sesuai model | `->parameters(['jenis-komoditas' => 'jenisKomoditas'])` |
|
||||||
|
| Method tambahan (`list`, `by{Induk}`, `verify`, `export`) | `Route::get/post/patch` biasa, **ditulis sebelum** `Route::resource` modul yang sama |
|
||||||
|
|
||||||
|
- Form hapus/ubah memakai method spoofing Blade (`@method('DELETE')`, `@method('PUT')`), dan AJAX mengirim method HTTP yang sesuai. Jangan memakai `GET` untuk menghapus.
|
||||||
|
- **Pengecualian:** Dashboard dan Laporan tidak memakai `Route::resource` (lihat Bagian 11). Halaman tunggal seperti Profil akun boleh memakai `Route::singleton('profil', ...)` atau route manual `edit`/`update`.
|
||||||
|
|
||||||
|
### 6.2 Aturan routing lainnya
|
||||||
|
|
||||||
|
- Semua route web di `routes/web.php`, API di `routes/api.php`.
|
||||||
|
- Kelompokkan per domain dengan `Route::middleware(...)->group()` dan komentar judul domain.
|
||||||
|
- Nested resource memakai `->shallow()`. Maksimal **dua tingkat** di URL (`/framework/{framework}/transformasi`). Hierarki lebih dalam (Framework → Transformasi → Aktivitas → Target) dipecah menjadi beberapa nested shallow resource berpasangan.
|
||||||
|
- **Satu route hanya didaftarkan satu kali.** Sebelum menambah route, cari dulu apakah sudah ada (`php artisan route:list --name=...`).
|
||||||
|
- Setelah login: `enumerator` → Dashboard Survei Petani; `fasilitator` → Profil akun (belum ada menu khusus); role lain → Dashboard Karakteristik Petani. Jangan mengubah logika ini tanpa izin.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Blade & Vuexy
|
||||||
|
|
||||||
|
### 7.1 Layout
|
||||||
|
|
||||||
|
- Semua halaman admin: `@extends('layout.main')` dan mengisi `@section('content')`.
|
||||||
|
- Halaman auth memakai `auth/layout.blade.php`.
|
||||||
|
- Jangan membuat layout baru; jangan menyalin `<head>`/sidebar ke halaman.
|
||||||
|
|
||||||
|
### 7.2 File view per pola
|
||||||
|
|
||||||
|
| File | Pola | Isi |
|
||||||
|
|---|---|---|
|
||||||
|
| `index.blade.php` | A, B, C | Tabel, pencarian, pagination (+ offcanvas form untuk Pola A) |
|
||||||
|
| `create.blade.php` | B, C | Form tambah, `@include('..._form')` |
|
||||||
|
| `edit.blade.php` | B, C | Form ubah, `@include('..._form')` |
|
||||||
|
| `show.blade.php` | B, C | Detail |
|
||||||
|
| `_form.blade.php` | A, B, C | Field form bersama untuk tambah dan ubah |
|
||||||
|
| `header.blade.php` | C | Kartu profil + navigasi tab |
|
||||||
|
|
||||||
|
- File `add.blade.php` dan `view.blade.php` **tidak boleh** dibuat lagi (ganti dengan `create` dan `show`).
|
||||||
|
- `_form.blade.php` menerima model (kosong saat tambah) dan memakai `old('field', $model->field)`.
|
||||||
|
|
||||||
|
### 7.3 Tampilan error validasi
|
||||||
|
|
||||||
|
```blade
|
||||||
|
<input type="text" name="nama"
|
||||||
|
class="form-control @error('nama') is-invalid @enderror"
|
||||||
|
value="{{ old('nama', $lahan->nama) }}">
|
||||||
|
@error('nama')
|
||||||
|
<div class="invalid-feedback">{{ $message }}</div>
|
||||||
|
@enderror
|
||||||
|
```
|
||||||
|
|
||||||
|
Untuk Pola A, JS menampilkan error dari respons 422 dengan class yang sama (`is-invalid` + `invalid-feedback`).
|
||||||
|
|
||||||
|
### 7.4 Komponen
|
||||||
|
|
||||||
|
- Elemen yang muncul di lebih dari dua halaman dijadikan Blade component di `resources/views/components/` (contoh yang ada: `summary-card`, `wilayah-card`, `wilayah-dashboard`).
|
||||||
|
- Utamakan komponen yang sudah ada sebelum membuat yang baru.
|
||||||
|
|
||||||
|
### 7.5 JavaScript & plugin
|
||||||
|
|
||||||
|
- JS halaman ditulis di **file terpisah per modul** (ikuti lokasi JS yang sudah dipakai project), dimuat lewat section/stack script di layout. Hindari blok `<script>` panjang di Blade.
|
||||||
|
- Semua request AJAX mengirim header `X-CSRF-TOKEN` dan `Accept: application/json`.
|
||||||
|
- URL di JS diambil dari `route()` yang dicetak Blade (mis. atribut `data-url`), **bukan** di-hardcode.
|
||||||
|
- Pakai plugin yang sudah tersedia di template Vuexy. Jangan menambah library baru tanpa izin. Pembagian peran:
|
||||||
|
|
||||||
|
| Kebutuhan | Plugin |
|
||||||
|
|---|---|
|
||||||
|
| Konfirmasi hapus | SweetAlert2 (jika terpasang) |
|
||||||
|
| Notifikasi sukses/gagal | Sesuai yang dipakai modul lain |
|
||||||
|
| Select dengan pencarian / dropdown berantai | Select2 (jika terpasang) |
|
||||||
|
| Input tanggal | Flatpickr (jika terpasang) |
|
||||||
|
| Grafik | ApexCharts |
|
||||||
|
| Peta | Leaflet |
|
||||||
|
| Ikon | Tabler Icons (`ti ti-*`) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Respons & pesan
|
||||||
|
|
||||||
|
### 8.1 Web (Pola B/C)
|
||||||
|
|
||||||
|
```php
|
||||||
|
return redirect()->route('lahan.index')->with('success', 'Lahan berhasil dihapus.');
|
||||||
|
return back()->withInput()->with('error', 'Data gagal disimpan.');
|
||||||
|
```
|
||||||
|
|
||||||
|
Kunci flash: `success`, `error`, `warning`, `info`.
|
||||||
|
|
||||||
|
### 8.2 JSON (Pola A dan AJAX web)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "success": true, "message": "Provinsi berhasil ditambahkan.", "data": { } }
|
||||||
|
```
|
||||||
|
|
||||||
|
- Error validasi: format 422 bawaan Laravel (`message` + `errors`).
|
||||||
|
- Error lain: `{ "success": false, "message": "..." }` dengan kode HTTP yang sesuai.
|
||||||
|
|
||||||
|
### 8.3 Pola kalimat pesan
|
||||||
|
|
||||||
|
`{Nama data} berhasil ditambahkan.` / `diperbarui.` / `dihapus.`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Otorisasi
|
||||||
|
|
||||||
|
- Hak akses berdasarkan role pada `Role` yang sudah ada. Ikuti mekanisme pembatasan role yang sudah dipakai (middleware/policy/gate yang ada); jangan membuat sistem permission baru.
|
||||||
|
- Penginput data petani adalah role `enumerator` (bukan `fasilitator`). `fasilitator` khusus pemateri workshop.
|
||||||
|
- Semua route `petani/...` dibungkus middleware `akses.petani` (`BatasiAksesPetani`): hanya `super_admin`, `admin`, `koordinator`, `enumerator`; koordinator hanya boleh mengakses petani dengan komoditas utama yang ditugaskan (`koordinator_komoditas`, cek lewat `User::bolehAksesPetani()`). Route tab petani baru wajib diletakkan di dalam grup ini.
|
||||||
|
- Menu sidebar hanya menampilkan item yang boleh diakses role pengguna, dan route-nya juga dilindungi di sisi server. Menyembunyikan menu saja tidak cukup.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. API (`app/Http/Controllers/Api/`)
|
||||||
|
|
||||||
|
- API dipakai aplikasi mobile/lapangan. **Kontrak API (URL, nama field, format respons) tidak boleh diubah** tanpa izin eksplisit, termasuk saat refaktor.
|
||||||
|
- Endpoint baru mengikuti pola dan format respons API yang sudah ada di folder `Api/`.
|
||||||
|
- Auth memakai Sanctum; login dibatasi rate limit (5/menit). Jangan longgarkan.
|
||||||
|
- Setiap endpoint baru harus terbaca rapi di dokumentasi Scramble (type-hint Form Request dan tipe return yang jelas).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Pengecualian
|
||||||
|
|
||||||
|
### 11.0 Prinsip umum Dashboard & Laporan
|
||||||
|
|
||||||
|
Dashboard dan laporan **bersifat baca (read-only)** dan bentuknya beragam, sehingga controller-nya **fleksibel**:
|
||||||
|
|
||||||
|
- **Nama method bebas**, asalkan deskriptif dan camelCase (`index`, `chartProduktivitas`, `petaSebaran`, `rekapPerDesa`, `exportExcel`).
|
||||||
|
- Boleh satu controller dengan banyak method, atau controller satu aksi (`__invoke`) jika halamannya sederhana.
|
||||||
|
- Route ditulis manual (`Route::get(...)`), **tidak** memakai `Route::resource`.
|
||||||
|
- Tidak ada `create/store/edit/update/destroy`. Jika sebuah dashboard butuh input data, input itu dibuat sebagai modul CRUD terpisah.
|
||||||
|
|
||||||
|
Yang **tetap berlaku** walau fleksibel:
|
||||||
|
|
||||||
|
- Lokasi folder per domain (Bagian 2), penamaan route kebab-case dengan prefix (`dashboard.*`, `laporan.*`).
|
||||||
|
- Validasi filter memakai Form Request jika filternya lebih dari 2–3 parameter.
|
||||||
|
- Query sederhana boleh di controller. Query agregat yang panjang, berat, atau dipakai di lebih dari satu tempat dipindah ke `app/Services/{Dashboard|Laporan}/`.
|
||||||
|
- Data yang ditampilkan tetap menghormati soft delete (data terhapus tidak ikut dihitung) dan global scope program.
|
||||||
|
- Pembatasan akses per role (Bagian 9).
|
||||||
|
|
||||||
|
### 11.1 Dashboard
|
||||||
|
|
||||||
|
- Controller: `app/Http/Controllers/Web/Dashboard/{Nama}Controller.php`.
|
||||||
|
- Pola yang disarankan: `index` mengembalikan halaman; data grafik/peta yang dimuat via AJAX memakai method bernama jelas yang mengembalikan JSON.
|
||||||
|
- Filter umum diterima lewat query string: program, wilayah (provinsi → desa), periode.
|
||||||
|
- Query berat di-cache (`Cache::remember`) dengan kunci yang memuat nilai filter.
|
||||||
|
- View: satu file per dashboard di `resources/views/dashboard/`. Grafik ApexCharts, peta Leaflet.
|
||||||
|
- Data eksternal (AlphaVantage di `InternationalMarketController`) wajib di-cache dan punya penanganan gagal (tampilkan pesan, jangan error 500).
|
||||||
|
|
||||||
|
```php
|
||||||
|
// routes/web.php
|
||||||
|
Route::prefix('dashboard')->name('dashboard.')->group(function () {
|
||||||
|
Route::get('produktivitas', [ProduktivitasController::class, 'index'])->name('produktivitas');
|
||||||
|
Route::get('produktivitas/chart-panen', [ProduktivitasController::class, 'chartPanen'])
|
||||||
|
->name('produktivitas.chart-panen');
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### 11.2 Laporan / reporting
|
||||||
|
|
||||||
|
- Controller: `app/Http/Controllers/Web/Laporan/{Nama}Controller.php`; view di `resources/views/laporan/{nama}/`.
|
||||||
|
- Pola yang disarankan per laporan: halaman filter + pratinjau (`index`), lalu method export per format (`exportExcel`, `exportPdf`). Semua menerima **filter yang sama** dengan pratinjau.
|
||||||
|
- Export kecil yang menempel pada satu modul CRUD atau dashboard boleh berupa method `export` di controller modul tersebut.
|
||||||
|
- Laporan yang lambat (data besar) boleh diproses di queue, lalu file diunduh setelah siap.
|
||||||
|
- Gunakan package export yang sudah terpasang di project. Jika belum ada, tanyakan dulu.
|
||||||
|
|
||||||
|
```php
|
||||||
|
Route::prefix('laporan')->name('laporan.')->group(function () {
|
||||||
|
Route::get('rekap-panen', [RekapPanenController::class, 'index'])->name('rekap-panen');
|
||||||
|
Route::get('rekap-panen/excel', [RekapPanenController::class, 'exportExcel'])->name('rekap-panen.excel');
|
||||||
|
Route::get('rekap-panen/pdf', [RekapPanenController::class, 'exportPdf'])->name('rekap-panen.pdf');
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### 11.3 Ekosistem (form builder dinamis)
|
||||||
|
|
||||||
|
- Modul `Ekosistem/` (Group, Tabel, Kolom, Formulir, Ringkasan, Value) memiliki struktur dinamis dan **dikecualikan** dari pola CRUD standar.
|
||||||
|
- Jangan direfaktor ke pola standar kecuali diminta secara eksplisit. Perubahan kecil tetap mengikuti aturan validasi, soft delete, dan kebersihan.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. Kebersihan repo
|
||||||
|
|
||||||
|
- **Dilarang** menyimpan file salinan/cadangan: `* copy.php`, `*Backup*`, `*01.php`, `*_old*`, dll. Riwayat disimpan di Git.
|
||||||
|
- Tidak ada kode yang dikomentari dalam jumlah besar ("kode mati"); hapus saja.
|
||||||
|
- Tidak ada route ganda (lihat Bagian 6).
|
||||||
|
- Satu commit = satu perubahan logis (satu modul/fitur). Pesan commit Bahasa Indonesia, singkat, diawali jenis: `feat:`, `fix:`, `refactor:`, `chore:`.
|
||||||
|
|
||||||
|
Masalah kebersihan yang **sudah diketahui** (selesaikan saat menyentuh area terkait):
|
||||||
|
|
||||||
|
- File duplikat/backup: `* copy.php`, `IndikatorPertanianBackupController`, `PanenController01`, `SummaryDashboardController copy.php`, file `composerlock` yang tidak semestinya.
|
||||||
|
- Route ganda: `users` (resource + manual), `users.verify` (dua kali), `jenis-komoditas.by-komoditas`, `varietas.by-jenis-komoditas`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13. Prosedur refaktor modul lama
|
||||||
|
|
||||||
|
Modul lama **wajib** disesuaikan dengan standar ini secara bertahap.
|
||||||
|
|
||||||
|
### 13.1 Aturan
|
||||||
|
|
||||||
|
1. **Satu modul per sesi/commit.** Jangan merefaktor beberapa modul sekaligus.
|
||||||
|
2. Jangan merefaktor modul yang sedang dikerjakan (ada perubahan belum di-commit) tanpa konfirmasi.
|
||||||
|
3. API **tidak** ikut diubah (Bagian 10).
|
||||||
|
4. Perilaku untuk pengguna harus tetap sama; refaktor bukan tempat menambah fitur.
|
||||||
|
|
||||||
|
### 13.2 Langkah
|
||||||
|
|
||||||
|
1. Simpan daftar route modul: `php artisan route:list --name={modul} > /tmp/before.txt`.
|
||||||
|
2. Tentukan pola target (A/B/C) sesuai 3.3.
|
||||||
|
3. Cari **semua** pemakaian nama route, URL, dan view lama di seluruh repo (Blade, JS, controller lain, sidebar), mis. `grep -rn "provinsi.view\|provinsi/add\|master.provinsi.add" app resources routes`.
|
||||||
|
4. Ubah:
|
||||||
|
- method `add` → `create`, `view` → `show`;
|
||||||
|
- file `add.blade.php` → `create.blade.php`, `view.blade.php` → `show.blade.php`;
|
||||||
|
- route manual → `Route::resource` (+ `only/except`, route tambahan di atasnya);
|
||||||
|
- validasi inline → Form Request (dengan `withoutTrashed()` pada unique);
|
||||||
|
- pastikan model memakai `SoftDeletes`, `deleted_by` terisi, dan cascade ke anak sudah ditulis;
|
||||||
|
- semua referensi yang ditemukan di langkah 3.
|
||||||
|
5. Bandingkan `route:list` sesudah dengan `/tmp/before.txt`; pastikan tidak ada route yang hilang tanpa sengaja.
|
||||||
|
6. Uji manual alur lengkap: index → tambah → detail → ubah → hapus (cek `deleted_at`, `deleted_by`, dan data anak).
|
||||||
|
7. Perbarui tabel status di 13.3.
|
||||||
|
|
||||||
|
### 13.3 Status migrasi modul
|
||||||
|
|
||||||
|
Status: `Belum` / `Proses` / `Selesai` / `Dikecualikan`
|
||||||
|
|
||||||
|
| Domain | Modul | Pola target | Status | Catatan |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| Master | Provinsi | A | Belum | Ada `list` + dropdown berantai |
|
||||||
|
| Master | Kabupaten/Kota | A | Belum | `by-provinsi` |
|
||||||
|
| Master | Kecamatan | A | Belum | `by-kabupaten` |
|
||||||
|
| Master | Desa/Kelurahan | A | Belum | `by-kecamatan` |
|
||||||
|
| Master | Komoditas | A | Belum | |
|
||||||
|
| Master | Jenis Komoditas | A | Belum | Route `by-komoditas` dobel |
|
||||||
|
| Master | Varietas | A | Belum | Route `by-jenis-komoditas` dobel |
|
||||||
|
| Master | Adat | A | Belum | |
|
||||||
|
| Master | Keluarga | ? | Belum | Tentukan pola saat refaktor |
|
||||||
|
| Produktivitas | Petani (+ semua tab) | C | Belum | |
|
||||||
|
| Produktivitas | Lahan | B | Belum | |
|
||||||
|
| Produktivitas | Tanaman | B | Belum | |
|
||||||
|
| Produktivitas | MapTanaman | ? | Belum | |
|
||||||
|
| Produktivitas | Produksi | B | Belum | |
|
||||||
|
| Produktivitas | Survey Kelompok Tani (+ tab) | C | Belum | |
|
||||||
|
| Produktivitas | Sebaran Petani | ? | Belum | Sedang dikerjakan, konfirmasi dulu |
|
||||||
|
| Program | Program, Program Komoditas, Program Lokasi | B | Belum | Pindah dari root ke `Program/` |
|
||||||
|
| Program | Framework → Transformasi → Aktivitas → Target | B | Belum | Nested shallow |
|
||||||
|
| Program | Realisasi | B | Belum | |
|
||||||
|
| Program | Verifikasi | ? | Belum | Sedang dikerjakan, konfirmasi dulu |
|
||||||
|
| Program | Workshop (+ Sesi, Peserta) | B | Belum | Sesi/Peserta sedang dikerjakan |
|
||||||
|
| Program | Kelompok Belajar Tani | B | Belum | |
|
||||||
|
| Bumdes | Profil, Kelembagaan, Survei | B | Belum | |
|
||||||
|
| Koperasi | Profil, Kelembagaan, Survei | B | Belum | |
|
||||||
|
| Konfigurasi | User | B | Belum | Route `users` & `users.verify` dobel |
|
||||||
|
| Konfigurasi | Profil akun | — | Belum | Hanya `edit`/`update` |
|
||||||
|
| Ekosistem | Semua | — | Dikecualikan | Lihat 11.3 |
|
||||||
|
| Dashboard | Semua | — | Dikecualikan | Ikuti 11.1 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 14. Checklist modul baru
|
||||||
|
|
||||||
|
- [ ] Pola ditentukan (A/B/C) sesuai 3.3
|
||||||
|
- [ ] Migration: `id`, kolom audit, `timestamps`, `softDeletes`, `deleted_by`; tanpa unique index
|
||||||
|
- [ ] Model: `$table` eksplisit, `$fillable`, `SoftDeletes`, `deleted_by` terisi, casts/Enum
|
||||||
|
- [ ] Relasi anak ditambahkan ke cascade `deleting` di model induk
|
||||||
|
- [ ] Form Request `Store`/`Update` dengan pesan Bahasa Indonesia dan unique `withoutTrashed()`
|
||||||
|
- [ ] Controller di `Web/{Domain}/` dengan nama method resmi saja
|
||||||
|
- [ ] Route: `Route::resource` (+ `only/except`, `shallow`), tidak dobel, route tambahan di atas resource
|
||||||
|
- [ ] View di `{domain}/{modul}/`: `index`, `create`, `edit`, `show`, `_form` sesuai pola
|
||||||
|
- [ ] JS di file terpisah, URL dari `route()`, CSRF terpasang
|
||||||
|
- [ ] Menu sidebar ditambahkan dan dibatasi per role; route dilindungi di server
|
||||||
|
- [ ] Respons/flash mengikuti Bagian 8
|
||||||
|
- [ ] Diuji manual: tambah, ubah, detail, hapus (cek `deleted_at`, `deleted_by`, data anak)
|
||||||
|
- [ ] Tidak ada file salinan/backup tertinggal
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\AksesPasar;
|
||||||
|
|
||||||
|
enum PraktikFermentasi: string
|
||||||
|
{
|
||||||
|
case RUTIN_SENDIRI = 'Ya, rutin — dilakukan sendiri/individual';
|
||||||
|
case RUTIN_KELOMPOK = 'Ya, rutin — bersama kelompok tani/kolektif';
|
||||||
|
case PERNAH_SENDIRI = 'Pernah mencoba — sendiri';
|
||||||
|
case PERNAH_KELOMPOK = 'Pernah mencoba — bersama kelompok';
|
||||||
|
case BELUM_PERNAH = 'Belum pernah';
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
['value' => self::RUTIN_SENDIRI->value, 'label' => 'Ya, rutin — dilakukan sendiri/individual'],
|
||||||
|
['value' => self::RUTIN_KELOMPOK->value, 'label' => 'Ya, rutin — bersama kelompok tani/kolektif'],
|
||||||
|
['value' => self::PERNAH_SENDIRI->value, 'label' => 'Pernah mencoba — sendiri'],
|
||||||
|
['value' => self::PERNAH_KELOMPOK->value, 'label' => 'Pernah mencoba — bersama kelompok'],
|
||||||
|
['value' => self::BELUM_PERNAH->value, 'label' => 'Belum pernah'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return array_column(self::cases(), 'value');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\AksesPasar;
|
||||||
|
|
||||||
|
enum SaluranTerdahulu: string
|
||||||
|
{
|
||||||
|
case TENGKULAK = 'Tengkulak–pedagang lokal';
|
||||||
|
case KOPERASI = 'Koperasi';
|
||||||
|
case BUMDES = 'BUMDes';
|
||||||
|
case LANGSUNG_PEMBELI = 'Langsung ke pembeli–eksportir';
|
||||||
|
case LAINNYA = 'Lainnya';
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
['value' => self::TENGKULAK->value, 'label' => 'Tengkulak–pedagang lokal'],
|
||||||
|
['value' => self::KOPERASI->value, 'label' => 'Koperasi'],
|
||||||
|
['value' => self::BUMDES->value, 'label' => 'BUMDes'],
|
||||||
|
['value' => self::LANGSUNG_PEMBELI->value, 'label' => 'Langsung ke pembeli–eksportir'],
|
||||||
|
['value' => self::LAINNYA->value, 'label' => 'Lainnya'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return array_column(self::cases(), 'value');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\AksesPasar;
|
||||||
|
|
||||||
|
enum SkorKesadaranPremium: int
|
||||||
|
{
|
||||||
|
case BELUM_PERNAH_DENGAR = 1;
|
||||||
|
case PERNAH_BELUM_PAHAM = 2;
|
||||||
|
case PERNAH_SEDIKIT_PAHAM = 3;
|
||||||
|
case SANGAT_PAHAM_BELUM_AKSES = 4;
|
||||||
|
case SUDAH_AKSES_KONSISTEN = 5;
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
['value' => self::BELUM_PERNAH_DENGAR->value, 'label' => 'Belum pernah dengar'],
|
||||||
|
['value' => self::PERNAH_BELUM_PAHAM->value, 'label' => 'Pernah dengar tapi belum paham syarat'],
|
||||||
|
['value' => self::PERNAH_SEDIKIT_PAHAM->value, 'label' => 'Pernah dengar & sedikit paham syarat'],
|
||||||
|
['value' => self::SANGAT_PAHAM_BELUM_AKSES->value, 'label' => 'Sangat paham syarat tapi belum pernah akses'],
|
||||||
|
['value' => self::SUDAH_AKSES_KONSISTEN->value, 'label' => 'Sudah akses & dapat harga premium konsisten'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return array_column(self::cases(), 'value');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\AksesPasar;
|
||||||
|
|
||||||
|
enum SkorPengalaman: int
|
||||||
|
{
|
||||||
|
case BELUM_PERNAH = 1;
|
||||||
|
case PERNAH_BERHENTI_TIDAK_ADA_AKSES = 2;
|
||||||
|
case PERNAH_BERHENTI_MUNGKIN_KEMBALI = 3;
|
||||||
|
case PERNAH_SESEKALI_JUAL = 4;
|
||||||
|
case PERNAH_MASIH_AKTIF = 5;
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
['value' => self::BELUM_PERNAH->value, 'label' => 'Belum pernah coba saluran lain'],
|
||||||
|
['value' => self::PERNAH_BERHENTI_TIDAK_ADA_AKSES->value, 'label' => 'Pernah, sudah berhenti & tidak ada akses'],
|
||||||
|
['value' => self::PERNAH_BERHENTI_MUNGKIN_KEMBALI->value, 'label' => 'Pernah, berhenti tapi masih mungkin kembali'],
|
||||||
|
['value' => self::PERNAH_SESEKALI_JUAL->value, 'label' => 'Pernah & sesekali masih jual ke sana'],
|
||||||
|
['value' => self::PERNAH_MASIH_AKTIF->value, 'label' => 'Pernah & masih punya akses aktif'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return array_column(self::cases(), 'value');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\AksesPasar;
|
||||||
|
|
||||||
|
enum TingkatKepercayaanPembeli: int
|
||||||
|
{
|
||||||
|
case SANGAT_PERCAYA = 1;
|
||||||
|
case CUKUP_PERCAYA = 2;
|
||||||
|
case RAGU_RAGU = 3;
|
||||||
|
case TIDAK_PERCAYA = 4;
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
['value' => self::SANGAT_PERCAYA->value, 'label' => 'Sangat percaya, berani langsung coba'],
|
||||||
|
['value' => self::CUKUP_PERCAYA->value, 'label' => 'Cukup percaya, tapi hati hati'],
|
||||||
|
['value' => self::RAGU_RAGU->value, 'label' => 'Ragu ragu'],
|
||||||
|
['value' => self::TIDAK_PERCAYA->value, 'label' => 'Tidak percaya, enggan mencoba'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return array_column(self::cases(), 'value');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums;
|
||||||
|
|
||||||
|
enum KeparahanSerangan: string
|
||||||
|
{
|
||||||
|
case RINGAN = 'ringan';
|
||||||
|
case SEDANG = 'sedang';
|
||||||
|
case BERAT = 'berat';
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match($this) {
|
||||||
|
self::RINGAN => 'Ringan, hanya beberapa tanaman',
|
||||||
|
self::SEDANG => 'Sedang, sebagian tanaman terdampak',
|
||||||
|
self::BERAT => 'Parah, sebagian besar tanaman terdampak',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,9 +4,27 @@ namespace App\Enums;
|
|||||||
|
|
||||||
enum KondisiTanaman: string
|
enum KondisiTanaman: string
|
||||||
{
|
{
|
||||||
case PRODUKTIF = 'productive';
|
case BAIK = 'baik';
|
||||||
case KURANG_PRODUKTIF = 'less_productive';
|
case SEDANG = 'sedang';
|
||||||
case TIDAK_PRODUKTIF = 'not_productive';
|
case BURUK = 'buruk';
|
||||||
case SAKIT = 'sick';
|
//case SAKIT= 'sakit';
|
||||||
case MATI = 'dead';
|
case MATI = 'mati';
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match($this) {
|
||||||
|
self::BAIK => 'Baik, tumbuh lebat, banyak buah',
|
||||||
|
self::SEDANG => 'Sedang, cukup tumbuh, ada masalah',
|
||||||
|
self::BURUK => 'Buruk, banyak tanaman tua dan lemah/tidak berbuah',
|
||||||
|
self::MATI => 'Mati, tanaman telah mati, tidak dapat tumbuh maupun berbuah',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
return match($this) {
|
||||||
self::TIDAK_SEKOLAH => 'Tidak Sekolah',
|
self::TIDAK_SEKOLAH => 'Tidak Sekolah',
|
||||||
self::SD => 'SD atau Sederajat',
|
self::SD => 'SD',
|
||||||
self::SMP => 'SMP atau Sederajat',
|
self::SMP => 'SMP',
|
||||||
self::SMA => 'SMA atau Sederajat',
|
self::SMA => 'SMA/SMK',
|
||||||
self::DIPLOMA => 'Diploma 1-3 (D1-D3)',
|
self::DIPLOMA => 'Diploma 1-3 (D1-D3)',
|
||||||
self::SARJANA => 'Sarjana (S1) atau sederajat',
|
self::SARJANA => 'Sarjana (S1)',
|
||||||
self::MAGISTER => 'Magister (S2) atau sederajat',
|
self::MAGISTER => 'Magister (S2)',
|
||||||
self::DOKTOR => 'Doktor (S3) atau sederajat',
|
self::DOKTOR => 'Doktor (S3)',
|
||||||
self::PESANTREN => 'Pesantren, Semiari, Wihara, atau Pendidikan Keagamaan Formal',
|
self::PESANTREN => 'Pesantren, Semiari, Wihara, atau Pendidikan Keagamaan Formal',
|
||||||
self::LAINNYA => 'Lainnya',
|
self::LAINNYA => 'Lainnya',
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\PendidikanPelatihan;
|
||||||
|
|
||||||
|
enum PelatihanGAP: string
|
||||||
|
{
|
||||||
|
case PEMANGKASAN = 'Pemangkasan/perawatan tanaman';
|
||||||
|
case PEMUPUKAN = 'Pemupukan/nutrisi';
|
||||||
|
case PENGENDALIAN = 'Pengendalian hama & penyakit';
|
||||||
|
case PRAKTIK = 'Praktik Berkebun yang Baik (GAP)';
|
||||||
|
case PENGOLAHAN = 'Pengolahan/pascapanen';
|
||||||
|
case PENYERBUKAN = 'Penyerbukan Manual (Vanili)';
|
||||||
|
case LAINNYA = 'Lainnya';
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return $this->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums;
|
||||||
|
|
||||||
|
enum PenyakitTanaman: string
|
||||||
|
{
|
||||||
|
case BUSUK_BUAH = 'busuk_buah';
|
||||||
|
case DAUN_MENGUNING = 'daun_menguning';
|
||||||
|
case DAUN_BERCAK = 'daun_bercak';
|
||||||
|
case TANAMAN_LAYU = 'tanaman_layu';
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::BUSUK_BUAH => 'Busuk Buah',
|
||||||
|
self::DAUN_MENGUNING => 'Daun Menguning',
|
||||||
|
self::DAUN_BERCAK => 'Daun Bercak',
|
||||||
|
self::TANAMAN_LAYU => 'Tanaman Layu',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\PraktikBudidaya;
|
||||||
|
|
||||||
|
enum Bulan: string
|
||||||
|
{
|
||||||
|
case JANUARI = 'Januari';
|
||||||
|
case FEBRUARI = 'Februari';
|
||||||
|
case MARET = 'Maret';
|
||||||
|
case APRIL = 'April';
|
||||||
|
case MEI = 'Mei';
|
||||||
|
case JUNI = 'Juni';
|
||||||
|
case JULI = 'Juli';
|
||||||
|
case AGUSTUS = 'Agustus';
|
||||||
|
case SEPTEMBER = 'September';
|
||||||
|
case OKTOBER = 'Oktober';
|
||||||
|
case NOVEMBER = 'November';
|
||||||
|
case DESEMBER = 'Desember';
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return $this->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\PraktikBudidaya;
|
||||||
|
|
||||||
|
enum CaraSortasi: string
|
||||||
|
{
|
||||||
|
case DIPILAH = 'Dipilah manual dengan tangan (memilih satu per satu buah/biji yang bagus dan membuang yang rusak)';
|
||||||
|
case ALAT_BANTU = 'Menggunakan alat bantu sederhana (tampah, ayakan, atau bak untuk memisahkan kotoran ringan)';
|
||||||
|
case PERENDAMAN = 'Metode perendaman/tenggelam (memisahkan buah mengapung dan tenggelam)';
|
||||||
|
case GRADING_KRITERIA = 'Grading berdasarkan kriteria (ukuran, warna, atau tingkat kematangan)';
|
||||||
|
case PEMBERSIHAN = 'Hanya pembersihan kotoran kasar (membuang dahan atau sampah besar)';
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match($this) {
|
||||||
|
self::DIPILAH => 'Dipilah manual dengan tangan (memilih satu per satu buah/biji yang bagus dan membuang yang rusak)',
|
||||||
|
self::ALAT_BANTU => 'Menggunakan alat bantu sederhana (tampah, ayakan, atau bak untuk memisahkan kotoran ringan)',
|
||||||
|
self::PERENDAMAN => 'Metode perendaman/tenggelam (memisahkan buah mengapung dan tenggelam)',
|
||||||
|
self::GRADING_KRITERIA => 'Grading berdasarkan kriteria (ukuran, warna, atau tingkat kematangan)',
|
||||||
|
self::PEMBERSIHAN => 'Hanya pembersihan kotoran kasar (membuang dahan atau sampah besar)',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\PraktikBudidaya;
|
||||||
|
|
||||||
|
enum DasarPenentuanPanen: int
|
||||||
|
{
|
||||||
|
case KEBUTUHAN_UANG = 1;
|
||||||
|
case UMUR_TANPA_KONDISI_BUAH = 2;
|
||||||
|
case BUAH_JATUH = 3;
|
||||||
|
case TINGKAT_KEPADATAN = 4;
|
||||||
|
case WARNA_KULIT_BUAH = 5;
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match($this) {
|
||||||
|
self::KEBUTUHAN_UANG => 'Kebutuhan uang mendesak, tanpa mempertimbangkan kematangan',
|
||||||
|
self::UMUR_TANPA_KONDISI_BUAH => 'Umur/jadwal tetap, tanpa melihat kondisi buah langsung',
|
||||||
|
self::BUAH_JATUH => 'Buah jatuh/matang pohon (sudah terlalu matang saat dipetik)',
|
||||||
|
self::TINGKAT_KEPADATAN => 'Bersuara saat diketuk atau tingkat kepadatan buah',
|
||||||
|
self::WARNA_KULIT_BUAH => 'Warna kulit buah (indikator paling akurat dan konsisten untuk menentukan kematangan optimal)',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\PraktikBudidaya;
|
||||||
|
|
||||||
|
enum EstimasiKehilanganHasil: string
|
||||||
|
{
|
||||||
|
case TIDAK_ADA = 'Tidak ada kehilangan';
|
||||||
|
case SEDIKIT = 'Sedikit, sekitar 25 % dari total panen';
|
||||||
|
case LUMAYAN = 'Lumayan, sekitar 50% dari total panen';
|
||||||
|
case BANYAK = 'Banyak, sekitar 75% dari total panen';
|
||||||
|
case HAMPIR_SEMUA = 'Hampir semua rusak, lebih dari 75%';
|
||||||
|
case TIDAK_TAHU = 'Hanya pembersihan kotoran kasar (membuang dahan atau sampah besar)';
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match($this) {
|
||||||
|
self::TIDAK_ADA => 'Tidak ada kehilangan',
|
||||||
|
self::SEDIKIT => 'Sedikit, sekitar 25 % dari total panen',
|
||||||
|
self::LUMAYAN => 'Lumayan, sekitar 50% dari total panen',
|
||||||
|
self::BANYAK => 'Banyak, sekitar 75% dari total panen',
|
||||||
|
self::HAMPIR_SEMUA => 'Hampir semua rusak, lebih dari 75%',
|
||||||
|
self::TIDAK_TAHU => 'Tidak tahu/sulit memperkirakan',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\PraktikBudidaya;
|
||||||
|
|
||||||
|
enum FrekuensiPemangkasanPendamping: string
|
||||||
|
{
|
||||||
|
case TIDAK_PERNAH = 'Tidak pernah memangkas sama sekali';
|
||||||
|
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';
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match($this) {
|
||||||
|
self::TIDAK_PERNAH => 'Tidak pernah memangkas sama sekali',
|
||||||
|
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',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\PraktikBudidaya;
|
||||||
|
|
||||||
|
enum FrekuensiPemangkasanUtama: int
|
||||||
|
{
|
||||||
|
case TIDAK_PERNAH = 1;
|
||||||
|
case TIDAK_RUTIN = 2;
|
||||||
|
case SATU_KALI_TAHUN = 3;
|
||||||
|
case DUA_KALI_TAHUN = 4;
|
||||||
|
case LEBIH_DUA_KALI_TAHUN = 5;
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match($this) {
|
||||||
|
self::TIDAK_PERNAH => 'Tidak pernah memangkas sama sekali',
|
||||||
|
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',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\PraktikBudidaya;
|
||||||
|
|
||||||
|
enum FrekuensiSanitasiLahan: int
|
||||||
|
{
|
||||||
|
case TIDAK_PERNAH = 1;
|
||||||
|
case SESEKALI = 2;
|
||||||
|
case SETIAP_PANEN = 3;
|
||||||
|
case RUTIN_TIDAK_TERJADWAL = 4;
|
||||||
|
case RUTIN_TERJADWAL = 5;
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match($this) {
|
||||||
|
self::TIDAK_PERNAH => 'Tidak pernah',
|
||||||
|
self::SESEKALI => 'Sesekali, tidak terjadwal',
|
||||||
|
self::SETIAP_PANEN => 'Setiap panen, tapi tidak menyeluruh',
|
||||||
|
self::RUTIN_TIDAK_TERJADWAL => 'Rutin, tapi tanpa jadwal yang jelas',
|
||||||
|
self::RUTIN_TERJADWAL => 'Rutin terjadwal dan menyeluruh',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\PraktikBudidaya;
|
||||||
|
|
||||||
|
enum IntensitasSeranganHamaPenyakit: string
|
||||||
|
{
|
||||||
|
case RINGAN = 'Ringan - hanya beberapa tanaman terdampak';
|
||||||
|
case SEDANG = 'Sedang - sebagian tanaman terdampak';
|
||||||
|
case PARAH = 'Parah - sebagian besar tanaman terdampak';
|
||||||
|
case BERVARIASI = 'Bervariasi - tergantung musim';
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match($this) {
|
||||||
|
self::RINGAN => 'Ringan - hanya beberapa tanaman terdampak',
|
||||||
|
self::SEDANG => 'Sedang - sebagian tanaman terdampak',
|
||||||
|
self::PARAH => 'Parah - sebagian besar tanaman terdampak',
|
||||||
|
self::BERVARIASI => 'Bervariasi - tergantung musim',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => $case->value)->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\PraktikBudidaya;
|
||||||
|
|
||||||
|
enum JenisPupuk: string
|
||||||
|
{
|
||||||
|
case TIDAK_MEMBERI = 'Tidak memberi pupuk';
|
||||||
|
case ORGANIK = 'Pupuk organik (kompos/kandang)';
|
||||||
|
case KIMIA = 'Pupuk kimia';
|
||||||
|
case KEDUANYA = 'Keduanya';
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match($this) {
|
||||||
|
self::TIDAK_MEMBERI => 'Tidak memberi pupuk',
|
||||||
|
self::ORGANIK => 'Pupuk organik (kompos/kandang)',
|
||||||
|
self::KIMIA => 'Pupuk kimia',
|
||||||
|
self::KEDUANYA => 'Keduanya',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\PraktikBudidaya;
|
||||||
|
|
||||||
|
enum KebutuhanAirPerMusim: string
|
||||||
|
{
|
||||||
|
case TIDAK_BUTUH = 'Tidak butuh, hujan cukup';
|
||||||
|
case KURANG = 'Kurang, sering kekurangan air';
|
||||||
|
case CUKUP = 'Cukup terpenuhi';
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match($this) {
|
||||||
|
self::TIDAK_BUTUH => 'Tidak butuh, hujan cukup',
|
||||||
|
self::KURANG => 'Kurang, sering kekurangan air',
|
||||||
|
self::CUKUP => 'Cukup terpenuhi',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\PraktikBudidaya;
|
||||||
|
|
||||||
|
enum KondisiPengelolaanAir: int
|
||||||
|
{
|
||||||
|
case KURANG_TIDAK_ADA_UPAYA = 1;
|
||||||
|
case KURANG_TIDAK_SUDAH_BERUPAYA = 2;
|
||||||
|
case CUKUP_DARI_HUJAN = 3;
|
||||||
|
case CUKUP_IRIGASI_PINJAM = 4;
|
||||||
|
case CUKUP_IRIGASI_SENDIRI = 5;
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match($this) {
|
||||||
|
self::KURANG_TIDAK_ADA_UPAYA => 'Kebutuhan air kurang terpenuhi, sering kekurangan air, tidak ada upaya mengatasinya',
|
||||||
|
self::KURANG_TIDAK_SUDAH_BERUPAYA => 'Kebutuhan air kurang terpenuhi, sering kekurangan air, tapi sudah mencoba mengandalkan sumber lain (sumur/sungai) walau belum cukup',
|
||||||
|
self::CUKUP_DARI_HUJAN => 'Kebutuhan air cukup terpenuhi hanya dari hujan, tanpa sistem irigasi tambahan',
|
||||||
|
self::CUKUP_IRIGASI_PINJAM => 'Kebutuhan air cukup terpenuhi, ada sistem irigasi tambahan (sungai/mata air/sumur) tapi pinjam',
|
||||||
|
self::CUKUP_IRIGASI_SENDIRI => 'Kebutuhan air cukup terpenuhi, punya akses irigasi sendiri (sungai/mata air/sumur) yang dikelola baik',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\PraktikBudidaya;
|
||||||
|
|
||||||
|
enum MetodePenangananHamaPenyakit: int
|
||||||
|
{
|
||||||
|
case TIDAK_ADA = 1;
|
||||||
|
case CARA_TRADISIONAL = 2;
|
||||||
|
case PESTISIDA_SANITASI = 3;
|
||||||
|
case KOMBINASI = 4;
|
||||||
|
case PHT = 5;
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match($this) {
|
||||||
|
self::TIDAK_ADA => 'Tidak ada penanganan',
|
||||||
|
self::CARA_TRADISIONAL => 'Cara tradisional (doa/ritual)',
|
||||||
|
self::PESTISIDA_SANITASI => 'Pestisida atau sanitasi',
|
||||||
|
self::KOMBINASI => 'Kombinasi pestisida dan sanitasi',
|
||||||
|
self::PHT => 'Pengendalian hama terpadu (PHT)',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => $case->value)->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\PraktikBudidaya;
|
||||||
|
|
||||||
|
enum PemahamanManfaatKonservasi: string
|
||||||
|
{
|
||||||
|
case YA_PAHAM = 'Ya, tahu manfaatnya';
|
||||||
|
case TIDAK_PAHAM = 'Dilakukan tapi tidak tahu manfaatnya, kebiasaan turun-temurun';
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match($this) {
|
||||||
|
self::YA_PAHAM => 'Ya, tahu manfaatnya',
|
||||||
|
self::TIDAK_PAHAM => 'Dilakukan tapi tidak tahu manfaatnya, kebiasaan turun-temurun',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\PraktikBudidaya;
|
||||||
|
|
||||||
|
enum PemberianPupuk: int
|
||||||
|
{
|
||||||
|
case TIDAK_PERNAH = 1;
|
||||||
|
case JENIS_DOSIS_WAKTU_TIDAK_MENENTU = 2;
|
||||||
|
case JENIS_SESUAI_DOSIS_TIDAK_MENENTU = 3;
|
||||||
|
case JENIS_DOSIS_SESUAI_WAKTU_CUKUP_TEPAT = 4;
|
||||||
|
case JENIS_DOSIS_SESUAI_WAKTU_TEPAT = 5;
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match($this) {
|
||||||
|
self::TIDAK_PERNAH => 'Tidak pernah memberi pupuk',
|
||||||
|
self::JENIS_DOSIS_WAKTU_TIDAK_MENENTU => 'Memberi pupuk, tapi jenis/dosis tidak menentu, waktu sembarangan',
|
||||||
|
self::JENIS_SESUAI_DOSIS_TIDAK_MENENTU => 'Memberi pupuk dengan jenis yang sesuai (organik/kimia/keduanya), tapi dosis atau waktu belum tepat',
|
||||||
|
self::JENIS_DOSIS_SESUAI_WAKTU_CUKUP_TEPAT => 'Memberi pupuk dengan jenis dan dosis yang sesuai, waktu cukup tepat',
|
||||||
|
self::JENIS_DOSIS_SESUAI_WAKTU_TEPAT => 'Memberi pupuk rutin dengan jenis tepat, dosis tepat, dan waktu tepat sesuai kebutuhan tanaman',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\PraktikBudidaya;
|
||||||
|
|
||||||
|
enum PenangananGulma: int
|
||||||
|
{
|
||||||
|
case DIBIARKAN = 1;
|
||||||
|
case DICABUT = 2;
|
||||||
|
case DISEMPROT = 3;
|
||||||
|
case RUTIN_DICABUT_TANPA_JADWAL = 4;
|
||||||
|
case RUTIN_TERJADWAL = 5;
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match($this) {
|
||||||
|
self::DIBIARKAN => 'Dibiarkan saja, tidak pernah ditangani',
|
||||||
|
self::DICABUT => 'Dicabut/dibersihkan sesekali, tidak terjadwal',
|
||||||
|
self::DISEMPROT => 'Disemprot herbisida sesekali, tidak terjadwal',
|
||||||
|
self::RUTIN_DICABUT_TANPA_JADWAL => 'Rutin dicabut/dipangkas, tapi tanpa jadwal yang jelas',
|
||||||
|
self::RUTIN_TERJADWAL => 'Rutin terjadwal dengan metode yang konsisten (dicabut/dipangkas/dikelola sebagai mulsa)',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\PraktikBudidaya;
|
||||||
|
|
||||||
|
enum PenangananSisaBuahRanting: string
|
||||||
|
{
|
||||||
|
case DIBIARKAN = 'Dibiarkan di kebun';
|
||||||
|
case DIKUMPULKAN = 'Dikumpulkan dan dibuang';
|
||||||
|
case KOMPOS = 'Dijadikan kompos';
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match($this) {
|
||||||
|
self::DIBIARKAN => 'Dibiarkan di kebun',
|
||||||
|
self::DIKUMPULKAN => 'Dikumpulkan dan dibuang',
|
||||||
|
self::KOMPOS => 'Dijadikan kompos',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\PraktikBudidaya;
|
||||||
|
|
||||||
|
enum PenyerbukanManual: string
|
||||||
|
{
|
||||||
|
case SELALU = 'Selalu, setiap musim berbunga';
|
||||||
|
case KADANG_KADANG = 'Kadang-kadang, tergantung kondisi';
|
||||||
|
case TIDAK_PERNAH = 'Tidak pernah';
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match($this) {
|
||||||
|
self::SELALU => 'Selalu, setiap musim berbunga',
|
||||||
|
self::KADANG_KADANG => 'Kadang-kadang, tergantung kondisi',
|
||||||
|
self::TIDAK_PERNAH => 'Tidak pernah',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\PraktikBudidaya;
|
||||||
|
|
||||||
|
enum PraktikSortasi: int
|
||||||
|
{
|
||||||
|
case TIDAK_PERNAH = 1;
|
||||||
|
case SANGAT_SEDERHANA = 2;
|
||||||
|
case TIDAK_SETIAP_PANEN = 3;
|
||||||
|
case SETIAP_PANEN = 4;
|
||||||
|
case KETAT = 5;
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match($this) {
|
||||||
|
self::TIDAK_PERNAH => 'Tidak pernah melakukan sortasi sama sekali',
|
||||||
|
self::SANGAT_SEDERHANA => 'Sortasi sangat sederhana, hanya membuang yang jelas-jelas rusak/busuk',
|
||||||
|
self::TIDAK_SETIAP_PANEN => 'Sortasi dilakukan tapi tidak setiap panen (kadang dilakukan, kadang tidak)',
|
||||||
|
self::SETIAP_PANEN => 'Sortasi dilakukan setiap panen, tapi standarnya belum konsisten',
|
||||||
|
self::KETAT => 'Sortasi ketat dengan standar yang jelas dan konsisten setiap panen',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\PraktikBudidaya;
|
||||||
|
|
||||||
|
enum TenagaKerja: string
|
||||||
|
{
|
||||||
|
case DIKERJAKAN_SENDIRI = 'Dikerjakan Sendiri';
|
||||||
|
case DIBANTU_ANGGOTA_KELUARGA = 'Dibantu Anggota Keluarga';
|
||||||
|
case UPAH_TENAGA_KERJA = 'Upah Tenaga Kerja';
|
||||||
|
case GOTONG_ROYONG = 'Gotong Royong/Swadaya Komunitas';
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match($this) {
|
||||||
|
self::DIKERJAKAN_SENDIRI => 'Dikerjakan Sendiri',
|
||||||
|
self::DIBANTU_ANGGOTA_KELUARGA => 'Dibantu Anggota Keluarga',
|
||||||
|
self::UPAH_TENAGA_KERJA => 'Upah Tenaga Kerja',
|
||||||
|
self::GOTONG_ROYONG => 'Gotong Royong/Swadaya Komunitas',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => $case->value)->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\PraktikBudidaya;
|
||||||
|
|
||||||
|
enum TindakanPanenReject: string
|
||||||
|
{
|
||||||
|
case BUANG = 'Dibuang';
|
||||||
|
case JUAL = 'Dijual ke tempat lain';
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match($this) {
|
||||||
|
self::BUANG => 'Dibuang',
|
||||||
|
self::JUAL => 'Dijual ke tempat lain',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\PraktikBudidaya;
|
||||||
|
|
||||||
|
enum TingkatPanenReject: int
|
||||||
|
{
|
||||||
|
case SERING = 1;
|
||||||
|
case CUKUP_SERING = 2;
|
||||||
|
case KADANG = 3;
|
||||||
|
case JARANG = 4;
|
||||||
|
case TIDAK_ADA = 5;
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match($this) {
|
||||||
|
self::SERING => 'Sering ditolak, lebih dari 50% hasil panen biasanya ditolak/tidak laku karena mutu',
|
||||||
|
self::CUKUP_SERING => 'Cukup sering, sekitar 25–50% hasil panen ditolak/tidak laku',
|
||||||
|
self::KADANG => 'Kadang ditolak, sekitar 10–25% hasil panen',
|
||||||
|
self::JARANG => 'Jarang ditolak, kurang dari 10% hasil panen',
|
||||||
|
self::TIDAK_ADA => 'Tidak ada yang ditolak, semua hasil panen sesuai standar pasar utama',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\PraktikBudidaya;
|
||||||
|
|
||||||
|
enum WaktuKemunculanTerparahHamaPenyakit: string
|
||||||
|
{
|
||||||
|
case HUJAN = 'Musim hujan';
|
||||||
|
case KEMARAU = 'Musim kemarau';
|
||||||
|
case SEPANJANG_TAHUN = 'Sepanjang tahun';
|
||||||
|
case TIDAK_TENTU = 'Tidak tentu';
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match($this) {
|
||||||
|
self::HUJAN => 'Musim hujan',
|
||||||
|
self::KEMARAU => 'Musim kemarau',
|
||||||
|
self::SEPANJANG_TAHUN => 'Sepanjang tahun',
|
||||||
|
self::TIDAK_TENTU => 'Tidak tentu',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => $case->value)->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
+17
-2
@@ -2,11 +2,26 @@
|
|||||||
|
|
||||||
namespace App\Enums;
|
namespace App\Enums;
|
||||||
|
|
||||||
enum Role: string
|
enum Role: string
|
||||||
{
|
{
|
||||||
case SUPER_ADMIN = 'super_admin';
|
case SUPER_ADMIN = 'super_admin';
|
||||||
case ADMIN = 'admin'; // admin program
|
case ADMIN = 'admin'; // admin program
|
||||||
case FASILITATOR = 'fasilitator';
|
case KOORDINATOR = 'koordinator'; // koordinator per komoditas
|
||||||
|
case ENUMERATOR = 'enumerator'; // penginput data petani di lapangan
|
||||||
|
case FASILITATOR = 'fasilitator'; // pemateri workshop
|
||||||
case PETANI = 'petani';
|
case PETANI = 'petani';
|
||||||
case OFFTAKER = 'offtaker';
|
case OFFTAKER = 'offtaker';
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::SUPER_ADMIN => 'Super Admin',
|
||||||
|
self::ADMIN => 'Admin',
|
||||||
|
self::KOORDINATOR => 'Koordinator',
|
||||||
|
self::ENUMERATOR => 'Enumerator',
|
||||||
|
self::FASILITATOR => 'Fasilitator',
|
||||||
|
self::PETANI => 'Petani',
|
||||||
|
self::OFFTAKER => 'Offtaker',
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums;
|
||||||
|
|
||||||
|
enum SeranganHama: string
|
||||||
|
{
|
||||||
|
case ULAT = 'ulat';
|
||||||
|
case BELALANG = 'belalang';
|
||||||
|
case KUTU = 'kutu';
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::ULAT => 'Ulat',
|
||||||
|
self::BELALANG => 'Belalang',
|
||||||
|
self::KUTU => 'Kutu',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\SosialEkonomi;
|
||||||
|
|
||||||
|
enum AlasanKreditDitolak: string
|
||||||
|
{
|
||||||
|
case TIDAK_PUNYA_JAMINAN_AGUNAN = 'tidak_punya_jaminan_agunan';
|
||||||
|
case PROSES_TERLALU_RUMIT = 'proses_terlalu_rumit';
|
||||||
|
case TAKUT_TIDAK_BISA_BAYAR = 'takut_tidak_bisa_bayar';
|
||||||
|
case TIDAK_TAHU_CARANYA = 'tidak_tahu_caranya';
|
||||||
|
case LAINNYA = 'lainnya';
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::TIDAK_PUNYA_JAMINAN_AGUNAN => 'Tidak punya jaminan-agunan',
|
||||||
|
self::PROSES_TERLALU_RUMIT => 'Proses terlalu rumit',
|
||||||
|
self::TAKUT_TIDAK_BISA_BAYAR => 'Takut tidak bisa bayar',
|
||||||
|
self::TIDAK_TAHU_CARANYA => 'Tidak tahu caranya',
|
||||||
|
self::LAINNYA => 'Lainnya',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return array_column(self::cases(), 'value');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\SosialEkonomi;
|
||||||
|
|
||||||
|
enum BisaBeliPupukBersubsidi: string
|
||||||
|
{
|
||||||
|
case YA = 'ya';
|
||||||
|
case TIDAK = 'tidak';
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::YA => 'Ya',
|
||||||
|
self::TIDAK => 'Tidak',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return array_column(self::cases(), 'value');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\SosialEkonomi;
|
||||||
|
|
||||||
|
enum JenisAsetProduktif: string
|
||||||
|
{
|
||||||
|
case TERNAK = 'ternak';
|
||||||
|
case KENDARAAN = 'kendaraan';
|
||||||
|
case ALAT_PERTANIAN = 'alat_pertanian';
|
||||||
|
case LAINNYA = 'lainnya';
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::TERNAK => 'Ternak',
|
||||||
|
self::KENDARAAN => 'Kendaraan',
|
||||||
|
self::ALAT_PERTANIAN => 'Alat pertanian',
|
||||||
|
self::LAINNYA => 'Lainnya',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return array_column(self::cases(), 'value');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\SosialEkonomi;
|
||||||
|
|
||||||
|
enum KategoriPendapatanKomoditas: string
|
||||||
|
{
|
||||||
|
case UTAMA = 'utama';
|
||||||
|
case LAINNYA = 'lainnya';
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return array_column(self::cases(), 'value');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\SosialEkonomi;
|
||||||
|
|
||||||
|
enum KeaktifanPertemuanKelompok: int
|
||||||
|
{
|
||||||
|
case TIDAK_PERNAH_HADIR = 1;
|
||||||
|
case JARANG_HADIR = 2;
|
||||||
|
case SERING_HADIR_LEBIH_BANYAK_DIAM = 3;
|
||||||
|
case HAMPIR_SELALU_HADIR_KADANG_DISKUSI = 4;
|
||||||
|
case SELALU_HADIR_AKTIF_DISKUSI = 5;
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::TIDAK_PERNAH_HADIR => 'Tidak pernah hadir',
|
||||||
|
self::JARANG_HADIR => 'Jarang hadir',
|
||||||
|
self::SERING_HADIR_LEBIH_BANYAK_DIAM => 'Sering hadir tapi lebih banyak diam',
|
||||||
|
self::HAMPIR_SELALU_HADIR_KADANG_DISKUSI => 'Hampir selalu hadir & kadang diskusi',
|
||||||
|
self::SELALU_HADIR_AKTIF_DISKUSI => 'Selalu hadir & aktif diskusi',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return array_column(self::cases(), 'value');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\SosialEkonomi;
|
||||||
|
|
||||||
|
enum KebebasanMemilihPembeli: int
|
||||||
|
{
|
||||||
|
case TIDAK_BEBAS_TERIKAT_SATU_PEMBELI = 1;
|
||||||
|
case AGAK_TERIKAT_ADA_TEKANAN = 2;
|
||||||
|
case AGAK_BEBAS_CENDERUNG_PEMBELI_SAMA = 3;
|
||||||
|
case CUKUP_BEBAS_SESEKALI_PINDAH = 4;
|
||||||
|
case SANGAT_BEBAS_PINDAH_KAPAN_SAJA = 5;
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::TIDAK_BEBAS_TERIKAT_SATU_PEMBELI => 'Tidak bebas, terikat satu pembeli',
|
||||||
|
self::AGAK_TERIKAT_ADA_TEKANAN => 'Agak terikat, ada tekanan',
|
||||||
|
self::AGAK_BEBAS_CENDERUNG_PEMBELI_SAMA => 'Agak bebas, cenderung pembeli sama',
|
||||||
|
self::CUKUP_BEBAS_SESEKALI_PINDAH => 'Cukup bebas, sesekali pindah',
|
||||||
|
self::SANGAT_BEBAS_PINDAH_KAPAN_SAJA => 'Sangat bebas, pindah kapan saja',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return array_column(self::cases(), 'value');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\SosialEkonomi;
|
||||||
|
|
||||||
|
enum KedekatanSesamaAnggota: string
|
||||||
|
{
|
||||||
|
case SANGAT_DEKAT = 'sangat_dekat';
|
||||||
|
case CUKUP_DEKAT = 'cukup_dekat';
|
||||||
|
case SEBATAS_KENAL = 'sebatas_kenal';
|
||||||
|
case TIDAK_BEGITU_KENAL = 'tidak_begitu_kenal';
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::SANGAT_DEKAT => 'Sangat dekat',
|
||||||
|
self::CUKUP_DEKAT => 'Cukup dekat',
|
||||||
|
self::SEBATAS_KENAL => 'Sebatas kenal',
|
||||||
|
self::TIDAK_BEGITU_KENAL => 'Tidak begitu kenal',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return array_column(self::cases(), 'value');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\SosialEkonomi;
|
||||||
|
|
||||||
|
enum KemauanIkutProgram: int
|
||||||
|
{
|
||||||
|
case TIDAK_MAU = 1;
|
||||||
|
case RAGU_RAGU = 2;
|
||||||
|
case MAU_PERLU_DIPASTIKAN_MANFAAT = 3;
|
||||||
|
case MAU_LIHAT_PROGRAM_DULU = 4;
|
||||||
|
case MAU_LANGSUNG_IKUT = 5;
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::TIDAK_MAU => 'Tidak mau',
|
||||||
|
self::RAGU_RAGU => 'Ragu-ragu',
|
||||||
|
self::MAU_PERLU_DIPASTIKAN_MANFAAT => 'Mau, perlu dipastikan manfaatnya',
|
||||||
|
self::MAU_LIHAT_PROGRAM_DULU => 'Mau, ingin lihat programnya dulu',
|
||||||
|
self::MAU_LANGSUNG_IKUT => 'Mau, langsung ikut',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return array_column(self::cases(), 'value');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\SosialEkonomi;
|
||||||
|
|
||||||
|
enum KemauanMencobaPraktikBaru: int
|
||||||
|
{
|
||||||
|
case TIDAK_MAU = 1;
|
||||||
|
case RAGU_RAGU = 2;
|
||||||
|
case MAU_LIHAT_HASIL_DULU = 3;
|
||||||
|
case MAU_LANGSUNG_COBA = 4;
|
||||||
|
case MAU_COBA_SAMPAI_BERHASIL = 5;
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::TIDAK_MAU => 'Tidak mau',
|
||||||
|
self::RAGU_RAGU => 'Ragu-ragu',
|
||||||
|
self::MAU_LIHAT_HASIL_DULU => 'Mau, lihat hasil dulu',
|
||||||
|
self::MAU_LANGSUNG_COBA => 'Mau langsung coba',
|
||||||
|
self::MAU_COBA_SAMPAI_BERHASIL => 'Mau coba sampai berhasil',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return array_column(self::cases(), 'value');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\SosialEkonomi;
|
||||||
|
|
||||||
|
enum KeterikatanHutangPanen: int
|
||||||
|
{
|
||||||
|
case TIDAK_BERSEDIA_MENJAWAB = 0;
|
||||||
|
case HUTANG_BESAR_HAMPIR_SELURUH_PANEN_TERIKAT = 1;
|
||||||
|
case HUTANG_LEBIH_DARI_SEPARUH_PANEN_TERIKAT = 2;
|
||||||
|
case HUTANG_SEBAGIAN_KECIL_TERIKAT = 3;
|
||||||
|
case HUTANG_KECIL_TIDAK_MENGIKAT_PANEN = 4;
|
||||||
|
case TIDAK_ADA = 5;
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::TIDAK_BERSEDIA_MENJAWAB => 'Tidak bersedia menjawab',
|
||||||
|
self::HUTANG_BESAR_HAMPIR_SELURUH_PANEN_TERIKAT => 'Ada hutang besar, hampir seluruh panen terikat',
|
||||||
|
self::HUTANG_LEBIH_DARI_SEPARUH_PANEN_TERIKAT => 'Ada hutang, >separuh panen terikat',
|
||||||
|
self::HUTANG_SEBAGIAN_KECIL_TERIKAT => 'Ada hutang, sebagian kecil terikat',
|
||||||
|
self::HUTANG_KECIL_TIDAK_MENGIKAT_PANEN => 'Ada hutang kecil, tidak mengikat panen',
|
||||||
|
self::TIDAK_ADA => 'Tidak ada',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return array_column(self::cases(), 'value');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\SosialEkonomi;
|
||||||
|
|
||||||
|
enum KeterlibatanProgramKeuanganFormal: int
|
||||||
|
{
|
||||||
|
case TIDAK_PERNAH_MENERIMA = 1;
|
||||||
|
case PERNAH_SATU_TIDAK_BERMANFAAT = 2;
|
||||||
|
case SEDANG_MENERIMA_SATU = 3;
|
||||||
|
case SEDANG_MENERIMA_LEBIH_DARI_SATU = 4;
|
||||||
|
case AKTIF_BEBERAPA_PROGRAM_BERMANFAAT = 5;
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::TIDAK_PERNAH_MENERIMA => 'Tidak pernah menerima',
|
||||||
|
self::PERNAH_SATU_TIDAK_BERMANFAAT => 'Pernah 1 program, tidak lagi & tidak bermanfaat',
|
||||||
|
self::SEDANG_MENERIMA_SATU => 'Sedang menerima 1 program',
|
||||||
|
self::SEDANG_MENERIMA_LEBIH_DARI_SATU => 'Sedang menerima >1 program',
|
||||||
|
self::AKTIF_BEBERAPA_PROGRAM_BERMANFAAT => 'Aktif beberapa program & merasakan manfaat nyata',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return array_column(self::cases(), 'value');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\SosialEkonomi;
|
||||||
|
|
||||||
|
enum KondisiAsetProduktif: int
|
||||||
|
{
|
||||||
|
case TIDAK_ADA = 1;
|
||||||
|
case ADA_SATU_KURANG_BAIK = 2;
|
||||||
|
case ADA_SATU_BAIK = 3;
|
||||||
|
case ADA_DUA_LEBIH_SEBAGIAN_KURANG_BAIK = 4;
|
||||||
|
case ADA_DUA_LEBIH_SEMUA_BAIK = 5;
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::TIDAK_ADA => 'Tidak ada',
|
||||||
|
self::ADA_SATU_KURANG_BAIK => 'Ada 1 jenis, kondisi kurang baik',
|
||||||
|
self::ADA_SATU_BAIK => 'Ada 1 jenis, kondisi baik',
|
||||||
|
self::ADA_DUA_LEBIH_SEBAGIAN_KURANG_BAIK => 'Ada 2+ jenis, sebagian kurang baik',
|
||||||
|
self::ADA_DUA_LEBIH_SEMUA_BAIK => 'Ada 2+ jenis, semua kondisi baik',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return array_column(self::cases(), 'value');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\SosialEkonomi;
|
||||||
|
|
||||||
|
enum ManfaatKelompokTani: int
|
||||||
|
{
|
||||||
|
case TIDAK_ADA_MANFAAT = 1;
|
||||||
|
case MANFAAT_SOSIAL_SAJA = 2;
|
||||||
|
case SEDIKIT_MANFAAT_PRODUKSI_PEMASARAN = 3;
|
||||||
|
case MANFAAT_CUKUP_TERASA = 4;
|
||||||
|
case MANFAAT_NYATA_SIGNIFIKAN = 5;
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::TIDAK_ADA_MANFAAT => 'Tidak ada manfaat',
|
||||||
|
self::MANFAAT_SOSIAL_SAJA => 'Manfaat sosial saja',
|
||||||
|
self::SEDIKIT_MANFAAT_PRODUKSI_PEMASARAN => 'Sedikit manfaat produksi-pemasaran',
|
||||||
|
self::MANFAAT_CUKUP_TERASA => 'Manfaat cukup terasa',
|
||||||
|
self::MANFAAT_NYATA_SIGNIFIKAN => 'Manfaat nyata & signifikan',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return array_column(self::cases(), 'value');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\SosialEkonomi;
|
||||||
|
|
||||||
|
enum MinatPembeliHargaStabil: string
|
||||||
|
{
|
||||||
|
case MAU_HARGA_STABIL_LEBIH_PENTING = 'mau_harga_stabil_lebih_penting';
|
||||||
|
case MAU_TAPI_INGIN_BANDINGKAN_DULU = 'mau_tapi_ingin_bandingkan_dulu';
|
||||||
|
case RAGU_RAGU = 'ragu_ragu';
|
||||||
|
case TIDAK_MAU_CARI_HARGA_TERBAIK = 'tidak_mau_cari_harga_terbaik';
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::MAU_HARGA_STABIL_LEBIH_PENTING => 'Mau-harga stabil lebih penting',
|
||||||
|
self::MAU_TAPI_INGIN_BANDINGKAN_DULU => 'Mau tapi ingin bandingkan dulu',
|
||||||
|
self::RAGU_RAGU => 'Ragu-ragu',
|
||||||
|
self::TIDAK_MAU_CARI_HARGA_TERBAIK => 'Tidak mau-cari harga terbaik tiap jual',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return array_column(self::cases(), 'value');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\SosialEkonomi;
|
||||||
|
|
||||||
|
enum OrientasiRencanaJangkaPanjang: int
|
||||||
|
{
|
||||||
|
case BELUM_KEPIKIRAN = 1;
|
||||||
|
case INGIN_TETAP_SEPERTI_SEKARANG = 2;
|
||||||
|
case INGIN_BERUBAH_BELUM_TAHU_CARANYA = 3;
|
||||||
|
case INGIN_SUDAH_MULAI_DIPIKIRKAN = 4;
|
||||||
|
case PUNYA_RENCANA_KONKRET = 5;
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::BELUM_KEPIKIRAN => 'Belum kepikiran',
|
||||||
|
self::INGIN_TETAP_SEPERTI_SEKARANG => 'Ingin tetap seperti sekarang',
|
||||||
|
self::INGIN_BERUBAH_BELUM_TAHU_CARANYA => 'Ada keinginan berubah tapi belum tahu caranya',
|
||||||
|
self::INGIN_SUDAH_MULAI_DIPIKIRKAN => 'Ada keinginan & sudah mulai dipikirkan',
|
||||||
|
self::PUNYA_RENCANA_KONKRET => 'Sudah punya rencana konkret',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return array_column(self::cases(), 'value');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\SosialEkonomi;
|
||||||
|
|
||||||
|
enum PanutanPetaniSukses: int
|
||||||
|
{
|
||||||
|
case TIDAK_ADA = 1;
|
||||||
|
case ADA_DENGAR_DARI_ORANG_LAIN = 2;
|
||||||
|
case TAHU_SIAPA_TIDAK_KENAL = 3;
|
||||||
|
case KENAL_BELUM_DISKUSI = 4;
|
||||||
|
case KENAL_PERNAH_DISKUSI_LANGSUNG = 5;
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::TIDAK_ADA => 'Tidak ada',
|
||||||
|
self::ADA_DENGAR_DARI_ORANG_LAIN => 'Ada, dengar dari orang lain saja',
|
||||||
|
self::TAHU_SIAPA_TIDAK_KENAL => 'Tahu siapa, tapi tidak kenal',
|
||||||
|
self::KENAL_BELUM_DISKUSI => 'Kenal, belum pernah diskusi',
|
||||||
|
self::KENAL_PERNAH_DISKUSI_LANGSUNG => 'Kenal & pernah diskusi langsung',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return array_column(self::cases(), 'value');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\SosialEkonomi;
|
||||||
|
|
||||||
|
enum PemahamanMutuHarga: int
|
||||||
|
{
|
||||||
|
case TIDAK_TAHU = 1;
|
||||||
|
case PERNAH_DENGAR_TIDAK_YAKIN = 2;
|
||||||
|
case TAHU_BEDA_BELUM_BUKTIKAN = 3;
|
||||||
|
case TAHU_PERNAH_HARGA_LEBIH_TINGGI = 4;
|
||||||
|
case TAHU_BETUL_BISA_JELASKAN_RINCI = 5;
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::TIDAK_TAHU => 'Tidak tahu',
|
||||||
|
self::PERNAH_DENGAR_TIDAK_YAKIN => 'Pernah dengar, tidak yakin',
|
||||||
|
self::TAHU_BEDA_BELUM_BUKTIKAN => 'Tahu ada beda, belum buktikan',
|
||||||
|
self::TAHU_PERNAH_HARGA_LEBIH_TINGGI => 'Tahu & pernah dapat harga lebih tinggi',
|
||||||
|
self::TAHU_BETUL_BISA_JELASKAN_RINCI => 'Tahu betul & bisa jelaskan rinci',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return array_column(self::cases(), 'value');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\SosialEkonomi;
|
||||||
|
|
||||||
|
enum PengalamanKreditKebun: int
|
||||||
|
{
|
||||||
|
case TIDAK_PERNAH_MENCOBA = 1;
|
||||||
|
case PERNAH_COBA_DITOLAK = 2;
|
||||||
|
case SEDANG_PROSES = 3;
|
||||||
|
case BERHASIL_MASIH_PELUNASAN = 4;
|
||||||
|
case BERHASIL_SUDAH_LUNAS = 5;
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::TIDAK_PERNAH_MENCOBA => 'Tidak pernah mencoba',
|
||||||
|
self::PERNAH_COBA_DITOLAK => 'Pernah coba tapi ditolak',
|
||||||
|
self::SEDANG_PROSES => 'Sedang dalam proses',
|
||||||
|
self::BERHASIL_MASIH_PELUNASAN => 'Berhasil, masih proses pelunasan',
|
||||||
|
self::BERHASIL_SUDAH_LUNAS => 'Berhasil & sudah lunas',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return array_column(self::cases(), 'value');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\SosialEkonomi;
|
||||||
|
|
||||||
|
enum PengetahuanHargaHasilOlah: string
|
||||||
|
{
|
||||||
|
case TIDAK_TAHU = 'tidak_tahu';
|
||||||
|
case PERNAH_DENGAR_TIDAK_TAHU_PENGARUH = 'pernah_dengar_tidak_tahu_pengaruh';
|
||||||
|
case TAHU_BEDA_HARGA_BELUM_COBA = 'tahu_beda_harga_belum_coba';
|
||||||
|
case PERNAH_JUAL_HASIL_OLAH_HARGA_LEBIH_TINGGI = 'pernah_jual_hasil_olah_harga_lebih_tinggi';
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::TIDAK_TAHU => 'Tidak tahu',
|
||||||
|
self::PERNAH_DENGAR_TIDAK_TAHU_PENGARUH => 'Pernah dengar tapi tidak tahu pengaruh ke harga',
|
||||||
|
self::TAHU_BEDA_HARGA_BELUM_COBA => 'Tahu ada beda harga tapi belum coba',
|
||||||
|
self::PERNAH_JUAL_HASIL_OLAH_HARGA_LEBIH_TINGGI => 'Sudah pernah jual hasil olah dengan harga lebih tinggi',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return array_column(self::cases(), 'value');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\SosialEkonomi;
|
||||||
|
|
||||||
|
enum PeranDalamKelompok: int
|
||||||
|
{
|
||||||
|
case ANGGOTA_PASIF = 1;
|
||||||
|
case ANGGOTA_BIASA_KADANG_DILIBATKAN = 2;
|
||||||
|
case ANGGOTA_BIASA_CUKUP_AKTIF = 3;
|
||||||
|
case PENGURUS_BELUM_BANYAK_TANGGUNG_JAWAB = 4;
|
||||||
|
case KETUA_PENGURUS_AKTIF = 5;
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::ANGGOTA_PASIF => 'Anggota pasif',
|
||||||
|
self::ANGGOTA_BIASA_KADANG_DILIBATKAN => 'Anggota biasa, kadang dilibatkan',
|
||||||
|
self::ANGGOTA_BIASA_CUKUP_AKTIF => 'Anggota biasa, cukup aktif',
|
||||||
|
self::PENGURUS_BELUM_BANYAK_TANGGUNG_JAWAB => 'Pengurus, belum banyak menjalankan tanggung jawab',
|
||||||
|
self::KETUA_PENGURUS_AKTIF => 'Ketua-pengurus aktif & menjalankan tanggung jawab nyata',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return array_column(self::cases(), 'value');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\SosialEkonomi;
|
||||||
|
|
||||||
|
enum PeriodePendapatan: string
|
||||||
|
{
|
||||||
|
case MUSIM = 'musim';
|
||||||
|
case TAHUN = 'tahun';
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return array_column(self::cases(), 'value');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\SosialEkonomi;
|
||||||
|
|
||||||
|
enum PernahBantuanNgoPerusahaan: string
|
||||||
|
{
|
||||||
|
case PERNAH = 'pernah';
|
||||||
|
case TIDAK_PERNAH = 'tidak_pernah';
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::PERNAH => 'Pernah',
|
||||||
|
self::TIDAK_PERNAH => 'Tidak pernah',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return array_column(self::cases(), 'value');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\SosialEkonomi;
|
||||||
|
|
||||||
|
enum PernahJualHargaLebihTinggi: string
|
||||||
|
{
|
||||||
|
case BELUM_PERNAH = 'belum_pernah';
|
||||||
|
case PERNAH = 'pernah';
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::BELUM_PERNAH => 'Belum pernah',
|
||||||
|
self::PERNAH => 'Pernah',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return array_column(self::cases(), 'value');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\SosialEkonomi;
|
||||||
|
|
||||||
|
enum PihakMembantuKonflik: string
|
||||||
|
{
|
||||||
|
case TIDAK_ADA_SELESAIKAN_SENDIRI = 'Tidak ada-selesaikan sendiri';
|
||||||
|
case ADA = 'Ada';
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return $this->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return array_column(self::cases(), 'value');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\SosialEkonomi;
|
||||||
|
|
||||||
|
enum PinjamanTokohAdatPanen: string
|
||||||
|
{
|
||||||
|
case TIDAK_ADA = 'tidak_ada';
|
||||||
|
case ADA = 'ada';
|
||||||
|
case TIDAK_BERSEDIA_MENJAWAB = 'tidak_bersedia_menjawab';
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::TIDAK_ADA => 'Tidak ada',
|
||||||
|
self::ADA => 'Ada',
|
||||||
|
self::TIDAK_BERSEDIA_MENJAWAB => 'Tidak bersedia menjawab',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return array_column(self::cases(), 'value');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\SosialEkonomi;
|
||||||
|
|
||||||
|
enum PraktikIjon: int
|
||||||
|
{
|
||||||
|
case TIDAK_BERSEDIA_MENJAWAB = 0;
|
||||||
|
case HAMPIR_SELURUH_PANEN_TERIKAT = 1;
|
||||||
|
case SEBAGIAN_BESAR_TERIKAT = 2;
|
||||||
|
case SESEKALI_SEBAGIAN_KECIL = 3;
|
||||||
|
case PERNAH_SUDAH_LAMA_TIDAK_LAGI = 4;
|
||||||
|
case TIDAK_PERNAH = 5;
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::TIDAK_BERSEDIA_MENJAWAB => 'Tidak bersedia menjawab',
|
||||||
|
self::HAMPIR_SELURUH_PANEN_TERIKAT => 'Hampir seluruh panen terikat ijon',
|
||||||
|
self::SEBAGIAN_BESAR_TERIKAT => 'Sebagian besar terikat',
|
||||||
|
self::SESEKALI_SEBAGIAN_KECIL => 'Sesekali-sebagian kecil',
|
||||||
|
self::PERNAH_SUDAH_LAMA_TIDAK_LAGI => 'Pernah, sudah lama tidak lagi',
|
||||||
|
self::TIDAK_PERNAH => 'Tidak pernah',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return array_column(self::cases(), 'value');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\SosialEkonomi;
|
||||||
|
|
||||||
|
enum SkorKecukupanPenghasilan: int
|
||||||
|
{
|
||||||
|
case SANGAT_TIDAK_CUKUP = 1;
|
||||||
|
case KURANG_SERING_BERHUTANG = 2;
|
||||||
|
case CUKUP_PAS_PASAN = 3;
|
||||||
|
case CUKUP_ADA_SISA = 4;
|
||||||
|
case LEBIH_DARI_CUKUP = 5;
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::SANGAT_TIDAK_CUKUP => 'Sangat tidak cukup',
|
||||||
|
self::KURANG_SERING_BERHUTANG => 'Kurang, sering berhutang',
|
||||||
|
self::CUKUP_PAS_PASAN => 'Cukup pas-pasan',
|
||||||
|
self::CUKUP_ADA_SISA => 'Cukup, kadang ada sisa',
|
||||||
|
self::LEBIH_DARI_CUKUP => 'Lebih dari cukup',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return array_column(self::cases(), 'value');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\SosialEkonomi;
|
||||||
|
|
||||||
|
enum SkorKejelasanStatusLahan: int
|
||||||
|
{
|
||||||
|
case TIDAK_ADA_DOKUMEN_ADA_SENGKETA = 1;
|
||||||
|
case ADA_DOKUMEN_MASIH_SENGKETA = 2;
|
||||||
|
case PERNAH_SENGKETA_SELESAI = 3;
|
||||||
|
case LAHAN_ADAT_TANPA_SENGKETA = 4;
|
||||||
|
case BERSERTIFIKAT_TANPA_SENGKETA = 5;
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::TIDAK_ADA_DOKUMEN_ADA_SENGKETA => 'Tidak ada dokumen & ada sengketa',
|
||||||
|
self::ADA_DOKUMEN_MASIH_SENGKETA => 'Ada dokumen tapi masih sengketa',
|
||||||
|
self::PERNAH_SENGKETA_SELESAI => 'Pernah sengketa, sudah selesai',
|
||||||
|
self::LAHAN_ADAT_TANPA_SENGKETA => 'Lahan adat/keluarga/perhutanan sosial tanpa sengketa',
|
||||||
|
self::BERSERTIFIKAT_TANPA_SENGKETA => 'Bersertifikat & tidak ada sengketa',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return array_column(self::cases(), 'value');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\SosialEkonomi;
|
||||||
|
|
||||||
|
enum SkorKemampuanMenabung: int
|
||||||
|
{
|
||||||
|
case TIDAK_BISA = 1;
|
||||||
|
case PERNAH_TERPAKAI = 2;
|
||||||
|
case KADANG_KADANG = 3;
|
||||||
|
case BISA_TIDAK_TETAP = 4;
|
||||||
|
case RUTIN_TETAP = 5;
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::TIDAK_BISA => 'Tidak bisa sama sekali',
|
||||||
|
self::PERNAH_TERPAKAI => 'Pernah tapi selalu terpakai',
|
||||||
|
self::KADANG_KADANG => 'Kadang-kadang',
|
||||||
|
self::BISA_TIDAK_TETAP => 'Bisa tapi tidak tetap',
|
||||||
|
self::RUTIN_TETAP => 'Rutin & jumlah tetap',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return array_column(self::cases(), 'value');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\SosialEkonomi;
|
||||||
|
|
||||||
|
enum SkorPeriodePaceklik: int
|
||||||
|
{
|
||||||
|
case LEBIH_DARI_EMPAT_BULAN = 1;
|
||||||
|
case TIGA_SAMPAI_EMPAT_BULAN = 2;
|
||||||
|
case SATU_SAMPAI_DUA_BULAN = 3;
|
||||||
|
case SEBENTAR_TIDAK_MENENTU = 4;
|
||||||
|
case TIDAK_ADA_STABIL = 5;
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::LEBIH_DARI_EMPAT_BULAN => '>4 bulan',
|
||||||
|
self::TIGA_SAMPAI_EMPAT_BULAN => '3-4 bulan',
|
||||||
|
self::SATU_SAMPAI_DUA_BULAN => '1-2 bulan',
|
||||||
|
self::SEBENTAR_TIDAK_MENENTU => 'Sebentar & tidak menentu',
|
||||||
|
self::TIDAK_ADA_STABIL => 'Tidak ada, stabil',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return array_column(self::cases(), 'value');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\SosialEkonomi;
|
||||||
|
|
||||||
|
enum StatusKartuTani: string
|
||||||
|
{
|
||||||
|
case BELUM_PUNYA = 'belum_punya';
|
||||||
|
case SUDAH_PUNYA_BELUM_AKTIF = 'sudah_punya_belum_aktif';
|
||||||
|
case SUDAH_PUNYA_AKTIF_DIGUNAKAN = 'sudah_punya_aktif_digunakan';
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::BELUM_PUNYA => 'Belum punya',
|
||||||
|
self::SUDAH_PUNYA_BELUM_AKTIF => 'Sudah punya tapi belum aktif',
|
||||||
|
self::SUDAH_PUNYA_AKTIF_DIGUNAKAN => 'Sudah punya & aktif digunakan',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return array_column(self::cases(), 'value');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\SosialEkonomi;
|
||||||
|
|
||||||
|
enum StatusKepemilikanLahanSosialEkonomi: string
|
||||||
|
{
|
||||||
|
case MILIK_PRIBADI = 'Milik pribadi';
|
||||||
|
case MILIK_ORANG_TUA_KELUARGA = 'Milik orang tua-keluarga';
|
||||||
|
case SEWA_BAGI_HASIL = 'Sewa-bagi hasil';
|
||||||
|
case LAHAN_ADAT = 'Lahan adat';
|
||||||
|
case PERHUTANAN_SOSIAL = 'Perhutanan sosial';
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return $this->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return array_column(self::cases(), 'value');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\SosialEkonomi;
|
||||||
|
|
||||||
|
enum StatusKonflik: string
|
||||||
|
{
|
||||||
|
case SUDAH_SELESAI = 'Sudah selesai';
|
||||||
|
case BELUM_MASIH_BERLANJUT = 'Belum, masih berlanjut';
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return $this->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return array_column(self::cases(), 'value');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\SosialEkonomi;
|
||||||
|
|
||||||
|
enum StatusProgramKeuanganFormal: string
|
||||||
|
{
|
||||||
|
case SEDANG_MENERIMA = 'sedang_menerima';
|
||||||
|
case PERNAH_TAPI_TIDAK_LAGI = 'pernah_tapi_tidak_lagi';
|
||||||
|
case BELUM_PERNAH = 'belum_pernah';
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::SEDANG_MENERIMA => 'Sedang Menerima',
|
||||||
|
self::PERNAH_TAPI_TIDAK_LAGI => 'Pernah tapi Tidak Lagi',
|
||||||
|
self::BELUM_PERNAH => 'Belum Pernah',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return array_column(self::cases(), 'value');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\SosialEkonomi;
|
||||||
|
|
||||||
|
enum StatusRekeningTabungan: int
|
||||||
|
{
|
||||||
|
case TIDAK_PUNYA_REKENING = 1;
|
||||||
|
case PUNYA_TIDAK_AKTIF = 2;
|
||||||
|
case PUNYA_KADANG_DIGUNAKAN = 3;
|
||||||
|
case PUNYA_CUKUP_RUTIN = 4;
|
||||||
|
case AKTIF_RUTIN_DIGUNAKAN = 5;
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::TIDAK_PUNYA_REKENING => 'Tidak punya rekening',
|
||||||
|
self::PUNYA_TIDAK_AKTIF => 'Punya tapi tidak aktif',
|
||||||
|
self::PUNYA_KADANG_DIGUNAKAN => 'Punya, kadang digunakan',
|
||||||
|
self::PUNYA_CUKUP_RUTIN => 'Punya, cukup rutin',
|
||||||
|
self::AKTIF_RUTIN_DIGUNAKAN => 'Aktif & rutin digunakan',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return array_column(self::cases(), 'value');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\SosialEkonomi;
|
||||||
|
|
||||||
|
enum TekananJualSegera: string
|
||||||
|
{
|
||||||
|
case TIDAK_ADA = 'tidak_ada';
|
||||||
|
case ADA = 'ada';
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::TIDAK_ADA => 'Tidak ada',
|
||||||
|
self::ADA => 'Ada',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return array_column(self::cases(), 'value');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\SosialEkonomi;
|
||||||
|
|
||||||
|
enum TerdaftarKelompokTani: string
|
||||||
|
{
|
||||||
|
case YA = 'ya';
|
||||||
|
case TIDAK = 'tidak';
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::YA => 'Ya',
|
||||||
|
self::TIDAK => 'Tidak',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return array_column(self::cases(), 'value');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\SosialEkonomi;
|
||||||
|
|
||||||
|
enum UsahaMutuTambahanHarga: string
|
||||||
|
{
|
||||||
|
case SEBANDING_COCOK_DICOBA = 'sebanding_cocok_dicoba';
|
||||||
|
case TIDAK_YAKIN = 'tidak_yakin';
|
||||||
|
case TIDAK_SEBANDING_TERLALU_BANYAK_USAHA = 'tidak_sebanding_terlalu_banyak_usaha';
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::SEBANDING_COCOK_DICOBA => 'Sebanding-cocok dicoba',
|
||||||
|
self::TIDAK_YAKIN => 'Tidak yakin',
|
||||||
|
self::TIDAK_SEBANDING_TERLALU_BANYAK_USAHA => 'Tidak sebanding-terlalu banyak usaha',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->map(fn ($case) => [
|
||||||
|
'value' => $case->value,
|
||||||
|
'label' => $case->label(),
|
||||||
|
])->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return array_column(self::cases(), 'value');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums;
|
||||||
|
|
||||||
|
// Status kelengkapan data profil petani.
|
||||||
|
// Draf → Lengkap (ditandai enumerator penginput) → Terverifikasi (oleh koordinator, menu menyusul).
|
||||||
|
enum StatusDataPetani: string
|
||||||
|
{
|
||||||
|
case DRAFT = 'draft';
|
||||||
|
case LENGKAP = 'lengkap';
|
||||||
|
case TERVERIFIKASI = 'terverifikasi';
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::DRAFT => 'Draf',
|
||||||
|
self::LENGKAP => 'Lengkap',
|
||||||
|
self::TERVERIFIKASI => 'Terverifikasi',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warna badge Vuexy (bg-label-{warna})
|
||||||
|
public function warna(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::DRAFT => 'secondary',
|
||||||
|
self::LENGKAP => 'info',
|
||||||
|
self::TERVERIFIKASI => 'success',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function values(): array
|
||||||
|
{
|
||||||
|
return array_column(self::cases(), 'value');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,7 +20,7 @@ class FormBasisDataController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function index(Request $request, string $tabelId): JsonResponse
|
public function index(Request $request, string $tabelId): JsonResponse
|
||||||
{
|
{
|
||||||
if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) {
|
if (!Gate::any(['super_admin', 'admin', 'enumerator'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ class TabelBasisDataController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function index(Request $request): AnonymousResourceCollection
|
public function index(Request $request): AnonymousResourceCollection
|
||||||
{
|
{
|
||||||
if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) {
|
if (!Gate::any(['super_admin', 'admin', 'enumerator'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ class TabelBasisDataController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function show(Request $request, string $id): JsonResponse
|
public function show(Request $request, string $id): JsonResponse
|
||||||
{
|
{
|
||||||
if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) {
|
if (!Gate::any(['super_admin', 'admin', 'enumerator'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,12 +16,10 @@ use App\Models\Core\Program;
|
|||||||
|
|
||||||
class ValueBasisDataController extends Controller
|
class ValueBasisDataController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* Display a listing of the resource.
|
|
||||||
*/
|
|
||||||
public function index(Request $request, string $tabelId): JsonResponse
|
public function index(Request $request, string $tabelId): JsonResponse
|
||||||
{
|
{
|
||||||
if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) {
|
if (!Gate::any(['super_admin', 'admin', 'enumerator'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,12 +147,73 @@ class ValueBasisDataController extends Controller
|
|||||||
return response()->json($values);
|
return response()->json($values);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private function prepareValidationRules($kolom)
|
||||||
* Store a newly created resource in storage.
|
{
|
||||||
*/
|
$rules = [];
|
||||||
|
foreach ($kolom as $k) {
|
||||||
|
if ($k->validasi && is_array($k->validasi)) {
|
||||||
|
$rules[$k->kolom] = $k->validasi;
|
||||||
|
|
||||||
|
if (!in_array('required', $k->validasi) && !in_array('nullable', $k->validasi)) {
|
||||||
|
$rules[$k->kolom][] = 'nullable';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!in_array('string', $k->validasi) && ($k->tipe == 'varchar' || $k->tipe == 'text')) {
|
||||||
|
$rules[$k->kolom][] = 'string';
|
||||||
|
}
|
||||||
|
if (!in_array('max', $k->validasi) && ($k->tipe == 'varchar')) {
|
||||||
|
$rules[$k->kolom][] = 'max:255';
|
||||||
|
}
|
||||||
|
if (!in_array('integer', $k->validasi) && $k->tipe == 'integer') {
|
||||||
|
$rules[$k->kolom][] = 'integer';
|
||||||
|
}
|
||||||
|
if (!in_array('boolean', $k->validasi) && $k->tipe == 'boolean') {
|
||||||
|
$rules[$k->kolom][] = 'boolean';
|
||||||
|
}
|
||||||
|
if (!in_array('date', $k->validasi) && ($k->tipe == 'date' || $k->tipe == 'datetime')) {
|
||||||
|
$rules[$k->kolom][] = 'date';
|
||||||
|
}
|
||||||
|
if ($k->tipe == 'foreign_key') {
|
||||||
|
$rules[$k->kolom][] = 'exists:' . $k->relasi_tabel . ',' . $k->relasi_kolom_id;
|
||||||
|
}
|
||||||
|
if ($k->tipe == 'enum') {
|
||||||
|
$enumValues = collect($k->enum_opsi)->pluck('value')->toArray();
|
||||||
|
$rules[$k->kolom][] = Rule::in($enumValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$rules[$k->kolom] = ['nullable'];
|
||||||
|
|
||||||
|
if ($k->tipe == 'varchar' || $k->tipe == 'text') {
|
||||||
|
$rules[$k->kolom][] = 'string';
|
||||||
|
}
|
||||||
|
if ($k->tipe == 'varchar') {
|
||||||
|
$rules[$k->kolom][] = 'max:255';
|
||||||
|
}
|
||||||
|
if ($k->tipe == 'integer') {
|
||||||
|
$rules[$k->kolom][] = 'integer';
|
||||||
|
}
|
||||||
|
if ($k->tipe == 'boolean') {
|
||||||
|
$rules[$k->kolom][] = 'boolean';
|
||||||
|
}
|
||||||
|
if ($k->tipe == 'date' || $k->tipe == 'datetime') {
|
||||||
|
$rules[$k->kolom][] = 'date';
|
||||||
|
}
|
||||||
|
if ($k->tipe == 'foreign_key') {
|
||||||
|
$rules[$k->kolom][] = 'exists:' . $k->relasi_tabel . ',' . $k->relasi_kolom_id;
|
||||||
|
}
|
||||||
|
if ($k->tipe == 'enum') {
|
||||||
|
$enumValues = collect($k->enum_opsi)->pluck('value')->toArray();
|
||||||
|
$rules[$k->kolom][] = Rule::in($enumValues);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $rules;
|
||||||
|
}
|
||||||
|
|
||||||
public function store(Request $request, string $tabelId): JsonResponse
|
public function store(Request $request, string $tabelId): JsonResponse
|
||||||
{
|
{
|
||||||
if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) {
|
if (!Gate::any(['super_admin', 'admin', 'enumerator'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,15 +242,7 @@ class ValueBasisDataController extends Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$validationRules = [];
|
$validated = $request->validate($this->prepareValidationRules($kolom));
|
||||||
foreach ($kolom as $k) {
|
|
||||||
if ($k->validasi && is_array($k->validasi)) {
|
|
||||||
$validationRules[$k->kolom] = $k->validasi;
|
|
||||||
} else {
|
|
||||||
$validationRules[$k->kolom] = ['nullable'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$validated = $request->validate($validationRules);
|
|
||||||
|
|
||||||
DB::transaction(function () use ($program, $tabelId, $validated, $kolom) {
|
DB::transaction(function () use ($program, $tabelId, $validated, $kolom) {
|
||||||
$maxId = ValueBasisData::where('tabel_id', $tabelId)
|
$maxId = ValueBasisData::where('tabel_id', $tabelId)
|
||||||
@@ -220,20 +271,14 @@ class ValueBasisDataController extends Controller
|
|||||||
return response()->json(['message' => 'Data berhasil disimpan.']);
|
return response()->json(['message' => 'Data berhasil disimpan.']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Display the specified resource.
|
|
||||||
*/
|
|
||||||
public function show(string $id)
|
public function show(string $id)
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the specified resource in storage.
|
|
||||||
*/
|
|
||||||
public function update(Request $request, string $tabelId, string $id): JsonResponse
|
public function update(Request $request, string $tabelId, string $id): JsonResponse
|
||||||
{
|
{
|
||||||
if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) {
|
if (!Gate::any(['super_admin', 'admin', 'enumerator'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,15 +316,7 @@ class ValueBasisDataController extends Controller
|
|||||||
abort(404, 'Data tidak ditemukan.');
|
abort(404, 'Data tidak ditemukan.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$validationRules = [];
|
$validated = $request->validate($this->prepareValidationRules($kolom));
|
||||||
foreach ($kolom as $k) {
|
|
||||||
if ($k->validasi && is_array($k->validasi)) {
|
|
||||||
$validationRules[$k->kolom] = $k->validasi;
|
|
||||||
} else {
|
|
||||||
$validationRules[$k->kolom] = ['nullable'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$validated = $request->validate($validationRules);
|
|
||||||
|
|
||||||
DB::transaction(function () use ($program, $tabelId, $validated, $kolom, $id, $values) {
|
DB::transaction(function () use ($program, $tabelId, $validated, $kolom, $id, $values) {
|
||||||
foreach ($validated as $kol => $val) {
|
foreach ($validated as $kol => $val) {
|
||||||
@@ -315,12 +352,9 @@ class ValueBasisDataController extends Controller
|
|||||||
return response()->json(['message' => 'Data berhasil diperbarui.']);
|
return response()->json(['message' => 'Data berhasil diperbarui.']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove the specified resource from storage.
|
|
||||||
*/
|
|
||||||
public function destroy(Request $request, string $tabelId, string $id): JsonResponse
|
public function destroy(Request $request, string $tabelId, string $id): JsonResponse
|
||||||
{
|
{
|
||||||
if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) {
|
if (!Gate::any(['super_admin', 'admin', 'enumerator'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ use App\Http\Resources\UserResource;
|
|||||||
|
|
||||||
class AuthController extends Controller
|
class AuthController extends Controller
|
||||||
{
|
{
|
||||||
public function login(Request $request): JSONResponse
|
public function login(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
$credentials = $request->validate([
|
$credentials = $request->validate([
|
||||||
'email' => ['required', 'email'],
|
'email' => ['required', 'email'],
|
||||||
@@ -27,6 +27,13 @@ class AuthController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
$user = Auth::user();
|
$user = Auth::user();
|
||||||
|
|
||||||
|
if ($user->status === 'pending') {
|
||||||
|
return response()->json([
|
||||||
|
'message' => 'Akun Anda masih berstatus pending. Silakan tunggu verifikasi dari Super Admin.'
|
||||||
|
], 403);
|
||||||
|
}
|
||||||
|
|
||||||
$user->tokens()->delete();
|
$user->tokens()->delete();
|
||||||
|
|
||||||
$token = $user->createToken('mobile-token')->plainTextToken;
|
$token = $user->createToken('mobile-token')->plainTextToken;
|
||||||
@@ -37,14 +44,13 @@ class AuthController extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function signup(Request $request): JSONResponse
|
public function signup(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
$credentials = $request->validate([
|
$credentials = $request->validate([
|
||||||
'name' => ['required', 'string', 'max:255'],
|
'name' => ['required', 'string', 'max:255'],
|
||||||
'email' => ['required', 'email', 'max:255', 'unique:users'],
|
'email' => ['required', 'email', 'max:255', 'unique:users'],
|
||||||
'password' => ['required', 'string', 'min:8', 'confirmed'],
|
'password' => ['required', 'string', 'min:8', 'confirmed'],
|
||||||
'username' => ['required', 'string', 'max:255'],
|
'username' => ['required', 'string', 'max:255'],
|
||||||
'role' => ['string'],
|
|
||||||
'uid' => ['required','string', 'unique:users'],
|
'uid' => ['required','string', 'unique:users'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -53,35 +59,27 @@ class AuthController extends Controller
|
|||||||
'email' => $credentials['email'],
|
'email' => $credentials['email'],
|
||||||
'password' => Hash::make($credentials['password']),
|
'password' => Hash::make($credentials['password']),
|
||||||
'username' => $credentials['username'],
|
'username' => $credentials['username'],
|
||||||
'role' => $credentials['role'],
|
|
||||||
'uid' => $credentials['uid'],
|
'uid' => $credentials['uid'],
|
||||||
|
'role' => null,
|
||||||
|
'status' => 'pending',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$profile = Profile::create([
|
$profile = Profile::create([
|
||||||
'nama' => $credentials['name'],
|
'nama' => $credentials['name'],
|
||||||
'email' => $credentials['email'],
|
'email' => $credentials['email'],
|
||||||
'role' => $credentials['role'],
|
|
||||||
'user_uid' => $credentials['uid'],
|
'user_uid' => $credentials['uid'],
|
||||||
'user_id' => $user->id,
|
'user_id' => $user->id,
|
||||||
'created_by' => $user->id,
|
'created_by' => $user->id,
|
||||||
'updated_by' => $user->id,
|
'updated_by' => $user->id,
|
||||||
|
|
||||||
|
|
||||||
|
'role' => null,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (!Auth::attempt($credentials)) {
|
|
||||||
return response()->json([
|
|
||||||
'message' => 'Email dan/atau password tidak sesuai'
|
|
||||||
], 401);
|
|
||||||
}
|
|
||||||
|
|
||||||
$user = Auth::user();
|
|
||||||
$user->tokens()->delete();
|
|
||||||
|
|
||||||
$token = $user->createToken('mobile-token')->plainTextToken;
|
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'access_token' => $token,
|
'message' => 'Registrasi berhasil. Akun Anda berstatus pending dan sedang menunggu verifikasi Super Admin.'
|
||||||
'token_type' => 'Bearer',
|
], 201);
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function me(Request $request): JSONResponse
|
public function me(Request $request): JSONResponse
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ class JenisKomoditasController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function index(Request $request): AnonymousResourceCollection
|
public function index(Request $request): AnonymousResourceCollection
|
||||||
{
|
{
|
||||||
if (!Gate::any(['petani', 'fasilitator', 'super_admin'])) {
|
if (!Gate::any(['petani', 'enumerator', 'super_admin'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,7 +63,7 @@ class JenisKomoditasController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function store(Request $request): JsonResponse
|
public function store(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
if (!Gate::any(['petani', 'fasilitator', 'super_admin'])) {
|
if (!Gate::any(['petani', 'enumerator', 'super_admin'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,7 +85,7 @@ class JenisKomoditasController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function show(string $id): JsonResponse
|
public function show(string $id): JsonResponse
|
||||||
{
|
{
|
||||||
if (!Gate::any(['petani', 'fasilitator', 'super_admin'])) {
|
if (!Gate::any(['petani', 'enumerator', 'super_admin'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,7 +107,7 @@ class JenisKomoditasController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function update(Request $request, string $id): JsonResponse
|
public function update(Request $request, string $id): JsonResponse
|
||||||
{
|
{
|
||||||
if (!Gate::any(['petani', 'fasilitator', 'super_admin'])) {
|
if (!Gate::any(['petani', 'enumerator', 'super_admin'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,7 +129,7 @@ class JenisKomoditasController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function destroy(string $id): JsonResponse
|
public function destroy(string $id): JsonResponse
|
||||||
{
|
{
|
||||||
if (!Gate::any(['petani', 'fasilitator', 'super_admin'])) {
|
if (!Gate::any(['petani', 'enumerator', 'super_admin'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ class DistributionController extends Controller
|
|||||||
{
|
{
|
||||||
public function index(): JsonResponse
|
public function index(): JsonResponse
|
||||||
{
|
{
|
||||||
if (!Gate::any(['super_admin', 'petani','fasilitator', 'offtaker'])) {
|
if (!Gate::any(['super_admin', 'petani','enumerator', 'offtaker'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ class DistributionController extends Controller
|
|||||||
|
|
||||||
public function store(Request $request): JsonResponse
|
public function store(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
if (!Gate::any(['super_admin', 'petani','fasilitator', 'offtaker'])) {
|
if (!Gate::any(['super_admin', 'petani','enumerator', 'offtaker'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ class DistributionController extends Controller
|
|||||||
|
|
||||||
public function update(Request $request, string $id): JsonResponse
|
public function update(Request $request, string $id): JsonResponse
|
||||||
{
|
{
|
||||||
if (!Gate::any(['super_admin', 'petani','fasilitator', 'offtaker'])) {
|
if (!Gate::any(['super_admin', 'petani','enumerator', 'offtaker'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +86,7 @@ class DistributionController extends Controller
|
|||||||
|
|
||||||
public function batchUpsert(Request $request): JsonResponse
|
public function batchUpsert(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
if (!Gate::any(['super_admin', 'petani','fasilitator', 'offtaker'])) {
|
if (!Gate::any(['super_admin', 'petani','enumerator', 'offtaker'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,12 +16,12 @@ use App\Enums\StatusLahan;
|
|||||||
|
|
||||||
class LahanController extends Controller
|
class LahanController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Display a listing of the resource.
|
* Display a listing of the resource.
|
||||||
*/
|
*/
|
||||||
public function index(Request $request): AnonymousResourceCollection
|
public function index(Request $request): AnonymousResourceCollection
|
||||||
{
|
{
|
||||||
if (!Gate::any(['petani', 'fasilitator','super_admin'])) {
|
if (!Gate::any(['petani', 'enumerator','super_admin'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,7 +77,7 @@ class LahanController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function store(Request $request): JsonResponse
|
public function store(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
if (!Gate::any(['petani', 'fasilitator','super_admin'])) {
|
if (!Gate::any(['petani', 'enumerator','super_admin'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,24 +91,25 @@ class LahanController extends Controller
|
|||||||
$request->merge(['profile_id' => (int) $request->profile_id]);
|
$request->merge(['profile_id' => (int) $request->profile_id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$profileRule = $user->role === 'fasilitator' ? ['required', 'numeric', 'exists:profiles,id'] : ['nullable'];
|
$profileRule = $user->role === 'enumerator' ? ['required', 'numeric', 'exists:profiles,id'] : ['nullable'];
|
||||||
|
|
||||||
$validated = $request->validate([
|
$validated = $request->validate([
|
||||||
'nama' => ['required', 'string', 'max:255'],
|
'nama' => ['required', 'string', 'max:255'],
|
||||||
'status_kepemilikan' => ['required', new Enum(StatusLahan::class)],
|
'status_kepemilikan' => ['required', new Enum(StatusLahan::class)],
|
||||||
'desa_kelurahan_id' => ['nullable', 'numeric', 'exists:master_desa_kelurahan,id'],
|
'desa_kelurahan_id' => ['nullable', 'numeric', 'exists:master_desa_kelurahan,id'],
|
||||||
'path' => ['nullable', 'array'],
|
'path' => ['nullable', 'string'],
|
||||||
'path.*' => ['array'],
|
|
||||||
'luas_lahan' => ['nullable', 'numeric'],
|
'luas_lahan' => ['nullable', 'numeric'],
|
||||||
'uid' => ['required', 'string'],
|
'uid' => ['required', 'string'],
|
||||||
'profile_id' => $profileRule,
|
'profile_id' => $profileRule,
|
||||||
|
'foto' => ['nullable', 'array'],
|
||||||
|
'foto.*' => ['image', 'mimes:jpeg,png,jpg', 'max:5120'], // Max 5MB
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$targetProfileId = $user->role === 'fasilitator'
|
$targetProfileId = $user->role === 'enumerator'
|
||||||
? $validated['profile_id']
|
? $validated['profile_id']
|
||||||
: $profile->id;
|
: $profile->id;
|
||||||
|
|
||||||
if ($user->role === 'fasilitator') {
|
if ($user->role === 'enumerator') {
|
||||||
$targetProfile = Profile::findOrFail($targetProfileId);
|
$targetProfile = Profile::findOrFail($targetProfileId);
|
||||||
|
|
||||||
if ($targetProfile->desa_kelurahan_id !== $profile->desa_kelurahan_id) {
|
if ($targetProfile->desa_kelurahan_id !== $profile->desa_kelurahan_id) {
|
||||||
@@ -116,14 +117,26 @@ class LahanController extends Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$desaKelurahanId = $user->role === 'fasilitator'
|
$desaKelurahanId = $user->role === 'enumerator'
|
||||||
? $profile->desa_kelurahan_id
|
? $profile->desa_kelurahan_id
|
||||||
: ($validated['desa_kelurahan_id'] ?? $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([
|
$lahan = Lahan::create([
|
||||||
...$validated,
|
'nama' => $validated['nama'],
|
||||||
'profile_id' => $profile->id,
|
'status_kepemilikan' => $validated['status_kepemilikan'],
|
||||||
'desa_kelurahan_id' => $desaKelurahanId,
|
'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))
|
return (new LahanResource($lahan))
|
||||||
@@ -136,14 +149,14 @@ class LahanController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function show(string $id): JsonResponse
|
public function show(string $id): JsonResponse
|
||||||
{
|
{
|
||||||
if (!Gate::any(['petani', 'fasilitator','super_admin'])) {
|
if (!Gate::any(['petani', 'enumerator','super_admin'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
$lahan = Lahan::with('desaKelurahan')->findOrFail($id);
|
$lahan = Lahan::with('desaKelurahan')->findOrFail($id);
|
||||||
if ($lahan->profile_id !== Auth::user()->profile->id && Auth::user()->role === 'petani') {
|
if ($lahan->profile_id !== Auth::user()->profile->id && Auth::user()->role === 'petani') {
|
||||||
return response()->json(['message' => 'Unauthorized'], 403);
|
return response()->json(['message' => 'Unauthorized'], 403);
|
||||||
} else if (Auth::user()->role === 'fasilitator' && $lahan->desa_kelurahan_id !== Auth::user()->profile->desa_kelurahan_id) {
|
} else if (Auth::user()->role === 'enumerator' && $lahan->desa_kelurahan_id !== Auth::user()->profile->desa_kelurahan_id) {
|
||||||
return response()->json(['message' => 'Unauthorized'], 403);
|
return response()->json(['message' => 'Unauthorized'], 403);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,16 +166,16 @@ class LahanController extends Controller
|
|||||||
/**
|
/**
|
||||||
* Update the specified resource in storage.
|
* 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'])) {
|
if (!Gate::any(['petani', 'enumerator','super_admin'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
$lahan = Lahan::findOrFail($id);
|
$lahan = Lahan::findOrFail($id);
|
||||||
if ($lahan->profile_id !== Auth::user()->profile->id && Auth::user()->role === 'petani') {
|
if ($lahan->profile_id !== Auth::user()->profile->id && Auth::user()->role === 'petani') {
|
||||||
return response()->json(['message' => 'Unauthorized'], 403);
|
return response()->json(['message' => 'Unauthorized'], 403);
|
||||||
} else if (Auth::user()->role === 'fasilitator' && $lahan->desa_kelurahan_id !== Auth::user()->profile->desa_kelurahan_id) {
|
} else if (Auth::user()->role === 'enumerator' && $lahan->desa_kelurahan_id !== Auth::user()->profile->desa_kelurahan_id) {
|
||||||
return response()->json(['message' => 'Unauthorized'], 403);
|
return response()->json(['message' => 'Unauthorized'], 403);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,9 +186,26 @@ class LahanController extends Controller
|
|||||||
'path' => ['nullable', 'array'],
|
'path' => ['nullable', 'array'],
|
||||||
'path.*' => ['array'],
|
'path.*' => ['array'],
|
||||||
'luas_lahan' => ['nullable', 'numeric'],
|
'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();
|
return (new LahanResource($lahan))->response();
|
||||||
}
|
}
|
||||||
@@ -185,14 +215,14 @@ class LahanController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function destroy(string $id): JsonResponse
|
public function destroy(string $id): JsonResponse
|
||||||
{
|
{
|
||||||
if (!Gate::any(['petani', 'fasilitator','super_admin'])) {
|
if (!Gate::any(['petani', 'enumerator','super_admin'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
$lahan = Lahan::findOrFail($id);
|
$lahan = Lahan::findOrFail($id);
|
||||||
if ($lahan->profile_id !== Auth::user()->profile->id && Auth::user()->role === 'petani') {
|
if ($lahan->profile_id !== Auth::user()->profile->id && Auth::user()->role === 'petani') {
|
||||||
return response()->json(['message' => 'Unauthorized'], 403);
|
return response()->json(['message' => 'Unauthorized'], 403);
|
||||||
} else if (Auth::user()->role === 'fasilitator' && $lahan->desa_kelurahan_id !== Auth::user()->profile->desa_kelurahan_id) {
|
} else if (Auth::user()->role === 'enumerator' && $lahan->desa_kelurahan_id !== Auth::user()->profile->desa_kelurahan_id) {
|
||||||
return response()->json(['message' => 'Unauthorized'], 403);
|
return response()->json(['message' => 'Unauthorized'], 403);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,7 +233,7 @@ class LahanController extends Controller
|
|||||||
|
|
||||||
public function batchUpsert(Request $request): JsonResponse
|
public function batchUpsert(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
if (!Gate::any(['petani', 'fasilitator','super_admin'])) {
|
if (!Gate::any(['petani', 'enumerator','super_admin'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class OfftakerController extends Controller
|
|||||||
{
|
{
|
||||||
public function index(Request $request): JsonResponse
|
public function index(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
if (!Gate::any(['super_admin', 'petani', 'fasilitator', 'admin','offtaker'])) {
|
if (!Gate::any(['super_admin', 'petani', 'enumerator', 'admin','offtaker'])) {
|
||||||
abort(403, 'Unauthorized');
|
abort(403, 'Unauthorized');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ class OfftakerController extends Controller
|
|||||||
|
|
||||||
public function store(Request $request): JsonResponse
|
public function store(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
if (!Gate::any(['super_admin', 'petani', 'fasilitator', 'admin','offtaker'])) {
|
if (!Gate::any(['super_admin', 'petani', 'enumerator', 'admin','offtaker'])) {
|
||||||
abort(403, 'Unauthorized');
|
abort(403, 'Unauthorized');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ class OfftakerController extends Controller
|
|||||||
|
|
||||||
public function show(string $id): JsonResponse
|
public function show(string $id): JsonResponse
|
||||||
{
|
{
|
||||||
if (!Gate::any(['super_admin', 'petani', 'fasilitator', 'admin','offtaker'])) {
|
if (!Gate::any(['super_admin', 'petani', 'enumerator', 'admin','offtaker'])) {
|
||||||
abort(403, 'Unauthorized');
|
abort(403, 'Unauthorized');
|
||||||
}
|
}
|
||||||
$offtaker = Offtaker::findOrFail($id);
|
$offtaker = Offtaker::findOrFail($id);
|
||||||
@@ -67,7 +67,7 @@ class OfftakerController extends Controller
|
|||||||
|
|
||||||
public function update(Request $request, string $id): JsonResponse
|
public function update(Request $request, string $id): JsonResponse
|
||||||
{
|
{
|
||||||
if (!Gate::any(['super_admin', 'petani', 'fasilitator', 'admin','offtaker'])) {
|
if (!Gate::any(['super_admin', 'petani', 'enumerator', 'admin','offtaker'])) {
|
||||||
abort(403, 'Unauthorized');
|
abort(403, 'Unauthorized');
|
||||||
}
|
}
|
||||||
$offtaker = Offtaker::findOrFail($id);
|
$offtaker = Offtaker::findOrFail($id);
|
||||||
@@ -87,7 +87,7 @@ class OfftakerController extends Controller
|
|||||||
|
|
||||||
public function destroy(string $id): JsonResponse
|
public function destroy(string $id): JsonResponse
|
||||||
{
|
{
|
||||||
if (!Gate::any(['super_admin', 'petani', 'fasilitator', 'admin','offtaker'])) {
|
if (!Gate::any(['super_admin', 'petani', 'enumerator', 'admin','offtaker'])) {
|
||||||
abort(403, 'Unauthorized');
|
abort(403, 'Unauthorized');
|
||||||
}
|
}
|
||||||
Offtaker::findOrFail($id)->delete();
|
Offtaker::findOrFail($id)->delete();
|
||||||
@@ -100,7 +100,7 @@ class OfftakerController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function batchUpsert(Request $request): JsonResponse
|
public function batchUpsert(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
if (!Gate::any(['super_admin', 'petani', 'fasilitator', 'admin','offtaker'])) {
|
if (!Gate::any(['super_admin', 'petani', 'enumerator', 'admin','offtaker'])) {
|
||||||
abort(403, 'Unauthorized');
|
abort(403, 'Unauthorized');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Controllers\Api;
|
namespace App\Http\Controllers\Api\Produktivitas;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
@@ -15,7 +15,7 @@ class PanenController extends Controller
|
|||||||
{
|
{
|
||||||
public function index(Request $request): JsonResponse
|
public function index(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
if (!Gate::any(['petani', 'fasilitator','super_admin'])) {
|
if (!Gate::any(['petani', 'enumerator','super_admin'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ class PanenController extends Controller
|
|||||||
|
|
||||||
public function store(Request $request): JsonResponse
|
public function store(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
if (!Gate::any(['petani', 'fasilitator', 'super_admin'])) {
|
if (!Gate::any(['petani', 'enumerator', 'super_admin'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ class PanenController extends Controller
|
|||||||
'land_description' => ['nullable', 'string'],
|
'land_description' => ['nullable', 'string'],
|
||||||
'harvest_date' => ['required', 'date'],
|
'harvest_date' => ['required', 'date'],
|
||||||
'commodities' => ['required', 'array', 'min:1'],
|
'commodities' => ['required', 'array', 'min:1'],
|
||||||
'commodities.*.plant_id' => ['nullable', 'integer'],
|
'commodities.*.tanaman_id' => ['nullable', 'integer'],
|
||||||
'commodities.*.commodity_name' => ['required', 'string'],
|
'commodities.*.commodity_name' => ['required', 'string'],
|
||||||
'commodities.*.weight_kg' => ['required', 'numeric'],
|
'commodities.*.weight_kg' => ['required', 'numeric'],
|
||||||
'notes' => ['nullable', 'string'],
|
'notes' => ['nullable', 'string'],
|
||||||
@@ -57,7 +57,7 @@ class PanenController extends Controller
|
|||||||
foreach ($validated['commodities'] as $commodity) {
|
foreach ($validated['commodities'] as $commodity) {
|
||||||
\App\Models\Map\PanenTanaman::create([
|
\App\Models\Map\PanenTanaman::create([
|
||||||
'panen_id' => $panen->id,
|
'panen_id' => $panen->id,
|
||||||
'tanaman_id' => $commodity['plant_id'] ?? null,
|
'tanaman_id' => $commodity['tanaman_id'] ?? null,
|
||||||
'commodity_name' => $commodity['commodity_name'],
|
'commodity_name' => $commodity['commodity_name'],
|
||||||
'weight_kg' => $commodity['weight_kg'],
|
'weight_kg' => $commodity['weight_kg'],
|
||||||
'sync_status' => 'synced',
|
'sync_status' => 'synced',
|
||||||
@@ -87,7 +87,7 @@ class PanenController extends Controller
|
|||||||
|
|
||||||
public function show(string $id): JsonResponse
|
public function show(string $id): JsonResponse
|
||||||
{
|
{
|
||||||
if (!Gate::any(['petani', 'fasilitator','super_admin'])) {
|
if (!Gate::any(['petani', 'enumerator','super_admin'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
$panen = Panen::findOrFail($id);
|
$panen = Panen::findOrFail($id);
|
||||||
@@ -96,7 +96,7 @@ class PanenController extends Controller
|
|||||||
|
|
||||||
public function update(Request $request, string $id): JsonResponse
|
public function update(Request $request, string $id): JsonResponse
|
||||||
{
|
{
|
||||||
if (!Gate::any(['petani', 'fasilitator','super_admin'])) {
|
if (!Gate::any(['petani', 'enumerator','super_admin'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
$panen = Panen::findOrFail($id);
|
$panen = Panen::findOrFail($id);
|
||||||
@@ -114,7 +114,7 @@ class PanenController extends Controller
|
|||||||
|
|
||||||
public function destroy(string $id): JsonResponse
|
public function destroy(string $id): JsonResponse
|
||||||
{
|
{
|
||||||
Gate::authorize(['petani', 'fasilitator']);
|
Gate::authorize(['petani', 'enumerator']);
|
||||||
Panen::findOrFail($id)->delete();
|
Panen::findOrFail($id)->delete();
|
||||||
return response()->json(null, 204);
|
return response()->json(null, 204);
|
||||||
}
|
}
|
||||||
@@ -124,7 +124,7 @@ class PanenController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function batchUpsert(Request $request): JsonResponse
|
public function batchUpsert(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
if (Gate::authorize(['petani', 'fasilitator','super_admin'])) {
|
if (Gate::authorize(['petani', 'enumerator','super_admin'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
$profileId = Auth::user()->profile->id;
|
$profileId = Auth::user()->profile->id;
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class PanenController01 extends Controller
|
|||||||
{
|
{
|
||||||
public function index(Request $request): JsonResponse
|
public function index(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
if (!Gate::any(['petani', 'fasilitator','super_admin'])) {
|
if (!Gate::any(['petani', 'enumerator','super_admin'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ class PanenController01 extends Controller
|
|||||||
|
|
||||||
public function store(Request $request): JsonResponse
|
public function store(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
if (!Gate::any(['petani', 'fasilitator','super_admin'])) {
|
if (!Gate::any(['petani', 'enumerator','super_admin'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ class PanenController01 extends Controller
|
|||||||
|
|
||||||
public function show(string $id): JsonResponse
|
public function show(string $id): JsonResponse
|
||||||
{
|
{
|
||||||
if (!Gate::any(['petani', 'fasilitator','super_admin'])) {
|
if (!Gate::any(['petani', 'enumerator','super_admin'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
$panen = Panen::findOrFail($id);
|
$panen = Panen::findOrFail($id);
|
||||||
@@ -67,7 +67,7 @@ class PanenController01 extends Controller
|
|||||||
|
|
||||||
public function update(Request $request, string $id): JsonResponse
|
public function update(Request $request, string $id): JsonResponse
|
||||||
{
|
{
|
||||||
if (!Gate::any(['petani', 'fasilitator','super_admin'])) {
|
if (!Gate::any(['petani', 'enumerator','super_admin'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
$panen = Panen::findOrFail($id);
|
$panen = Panen::findOrFail($id);
|
||||||
@@ -85,7 +85,7 @@ class PanenController01 extends Controller
|
|||||||
|
|
||||||
public function destroy(string $id): JsonResponse
|
public function destroy(string $id): JsonResponse
|
||||||
{
|
{
|
||||||
Gate::authorize(['petani', 'fasilitator']);
|
Gate::authorize(['petani', 'enumerator']);
|
||||||
Panen::findOrFail($id)->delete();
|
Panen::findOrFail($id)->delete();
|
||||||
return response()->json(null, 204);
|
return response()->json(null, 204);
|
||||||
}
|
}
|
||||||
@@ -95,7 +95,7 @@ class PanenController01 extends Controller
|
|||||||
*/
|
*/
|
||||||
public function batchUpsert(Request $request): JsonResponse
|
public function batchUpsert(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
if (Gate::authorize(['petani', 'fasilitator','super_admin'])) {
|
if (Gate::authorize(['petani', 'enumerator','super_admin'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
$profileId = Auth::user()->profile->id;
|
$profileId = Auth::user()->profile->id;
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class ProfileController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function index(Request $request)
|
public function index(Request $request)
|
||||||
{
|
{
|
||||||
if(!Gate::any(['fasilitator', 'super_admin'])) {
|
if(!Gate::any(['enumerator', 'super_admin'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ class ProfileController extends Controller
|
|||||||
$profileId = $user->role === 'petani' ? $profile->id : $requestID;
|
$profileId = $user->role === 'petani' ? $profile->id : $requestID;
|
||||||
|
|
||||||
$desaKelurahanId = null;
|
$desaKelurahanId = null;
|
||||||
if ($profile->role === 'fasilitator' || $profile->role === 'super_admin') {
|
if ($profile->role === 'enumerator' || $profile->role === 'super_admin') {
|
||||||
$desaKelurahanId = $profile->desa_kelurahan_id;
|
$desaKelurahanId = $profile->desa_kelurahan_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,7 +95,7 @@ class ProfileController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function store(Request $request)
|
public function store(Request $request)
|
||||||
{
|
{
|
||||||
if(!Gate::any(['fasilitator','super_admin'])){
|
if(!Gate::any(['enumerator','super_admin'])){
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,7 +139,7 @@ class ProfileController extends Controller
|
|||||||
public function show(string $id): JSONResponse
|
public function show(string $id): JSONResponse
|
||||||
{
|
{
|
||||||
// attempt to access profile, only for allowed roles
|
// attempt to access profile, only for allowed roles
|
||||||
if (!Gate::any(['petani', 'fasilitator', 'offtaker', 'super_admin'])) {
|
if (!Gate::any(['petani', 'enumerator', 'offtaker', 'super_admin'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,7 +166,7 @@ class ProfileController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function update(Request $request, string $id): JSONResponse
|
public function update(Request $request, string $id): JSONResponse
|
||||||
{
|
{
|
||||||
if (!Gate::any(['petani', 'fasilitator', 'offtaker', 'super_admin'])) {
|
if (!Gate::any(['petani', 'enumerator', 'offtaker', 'super_admin'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,7 +177,7 @@ class ProfileController extends Controller
|
|||||||
|
|
||||||
$profile = Profile::findOrFail($id);
|
$profile = Profile::findOrFail($id);
|
||||||
|
|
||||||
if ($user->role === 'fasilitator' && $profile->desa_kelurahan_id != $user->profile->desa_kelurahan_id) {
|
if ($user->role === 'enumerator' && $profile->desa_kelurahan_id != $user->profile->desa_kelurahan_id) {
|
||||||
abort(403, 'Unauthorized');
|
abort(403, 'Unauthorized');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,7 +239,7 @@ class ProfileController extends Controller
|
|||||||
|
|
||||||
public function downloadFile(string $id, string $type): JsonResponse | BinaryFileResponse
|
public function downloadFile(string $id, string $type): JsonResponse | BinaryFileResponse
|
||||||
{
|
{
|
||||||
if (!Gate::any(['petani', 'fasilitator', 'super_admin'])) {
|
if (!Gate::any(['petani', 'enumerator', 'super_admin'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,7 +260,7 @@ class ProfileController extends Controller
|
|||||||
|
|
||||||
public function verify(Request $request, string $id): JSONResponse
|
public function verify(Request $request, string $id): JSONResponse
|
||||||
{
|
{
|
||||||
if (!Gate::any(['fasilitator'])) {
|
if (!Gate::any(['enumerator'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ class TanamanController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function index(Request $request): AnonymousResourceCollection
|
public function index(Request $request): AnonymousResourceCollection
|
||||||
{
|
{
|
||||||
if (!Gate::any(['petani', 'fasilitator','super_admin'])) {
|
if (!Gate::any(['petani', 'enumerator','super_admin'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
$user = Auth::user();
|
$user = Auth::user();
|
||||||
@@ -91,7 +91,7 @@ class TanamanController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function store(Request $request): JsonResponse
|
public function store(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
if (!Gate::any(['petani', 'fasilitator','super_admin'])) {
|
if (!Gate::any(['petani', 'enumerator','super_admin'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,7 +135,7 @@ class TanamanController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function show(string $id): JsonResponse
|
public function show(string $id): JsonResponse
|
||||||
{
|
{
|
||||||
if (!Gate::any(['petani', 'fasilitator','super_admin'])) {
|
if (!Gate::any(['petani', 'enumerator','super_admin'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,7 +160,7 @@ class TanamanController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function update(Request $request, string $id): JsonResponse
|
public function update(Request $request, string $id): JsonResponse
|
||||||
{
|
{
|
||||||
if (!Gate::any(['petani', 'fasilitator','super_admin'])) {
|
if (!Gate::any(['petani', 'enumerator','super_admin'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
$tanaman = Tanaman::findOrFail($id);
|
$tanaman = Tanaman::findOrFail($id);
|
||||||
@@ -195,7 +195,7 @@ class TanamanController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function destroy(string $id): JsonResponse
|
public function destroy(string $id): JsonResponse
|
||||||
{
|
{
|
||||||
if (!Gate::any(['petani', 'fasilitator','super_admin'])) {
|
if (!Gate::any(['petani', 'enumerator','super_admin'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
$tanaman = Tanaman::findOrFail($id);
|
$tanaman = Tanaman::findOrFail($id);
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ class TanamanController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function index(Request $request): AnonymousResourceCollection
|
public function index(Request $request): AnonymousResourceCollection
|
||||||
{
|
{
|
||||||
if (!Gate::any(['petani', 'fasilitator','super_admin'])) {
|
if (!Gate::any(['petani', 'enumerator','super_admin'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
$user = Auth::user();
|
$user = Auth::user();
|
||||||
@@ -91,44 +91,44 @@ class TanamanController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function store(Request $request): JsonResponse
|
public function store(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
if (!Gate::any(['petani', 'fasilitator','super_admin'])) {
|
if (!Gate::any(['petani', 'enumerator','super_admin'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
$user = Auth::user();
|
$user = Auth::user();
|
||||||
$profile = $user->profile;
|
$profileId = $user->role === 'petani' ? $user->profile->id : $request->profile_id;
|
||||||
$profileId = $user->role === 'petani' ? $profile->id : $request->profile_id;
|
$lahanId = $request->input('lahan_id') ?? $request->input('land_id') ?? $request->input('lahan.id');
|
||||||
|
|
||||||
$request->merge([
|
$request->merge([
|
||||||
'komoditas_id' => $request->input('komoditas.id') ?? $request->input('komoditas_id'),
|
'komoditas_id' => $request->input('komoditas.id') ?? $request->input('komoditas_id'),
|
||||||
'lahan_id' => $request->input('lahan.id') ?? $request->input('lahan_id'),
|
'lahan_id' => $lahanId,
|
||||||
'profile_id' => $profileId,
|
'profile_id' => $profileId,
|
||||||
|
'uid' => $request->input('uid'),
|
||||||
|
'created_by' => $user->id,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$validated = $request->validate([
|
$validated = $request->validate([
|
||||||
'nama' => ['required', 'string', 'max:255'],
|
'nama' => ['required', 'string', 'max:255'],
|
||||||
'komoditas_id' => ['required', 'exists:master_komoditas,id'],
|
'komoditas_id' => ['required', 'exists:master_komoditas,id'],
|
||||||
'lahan_id' => [
|
'lahan_id' => ['required', 'exists:map_lahan,id'],
|
||||||
'required',
|
'profile_id' => ['required', 'exists:profiles,id'],
|
||||||
Rule::exists('map_lahan', 'id')->where(function ($query) use ($profileId) {
|
'latitude' => ['nullable', 'numeric', 'between:-90,90', 'required_with:longitude'],
|
||||||
$query->where('profile_id', $profileId)
|
'longitude' => ['nullable', 'numeric', 'between:-180,180', 'required_with:latitude'],
|
||||||
->whereNull('deleted_at');
|
'kondisi_tanaman' => ['nullable', 'string', new Enum(KondisiTanaman::class)],
|
||||||
}),
|
'uid' => ['required', 'string'],
|
||||||
],
|
|
||||||
'latitude' => ['nullable', 'numeric', 'between:-90,90', 'required_with:longitude'],
|
|
||||||
'longitude' => ['nullable ', 'numeric', 'between:-180,180', 'required_with:latitude'],
|
|
||||||
'kondisi_tanaman' => ['nullable', 'string', new Enum(KondisiTanaman::class)],
|
|
||||||
'uid' => ['required', 'string'],
|
|
||||||
'jenis_komoditas_id' => ['nullable', 'exists:jenis_komoditas,id'],
|
'jenis_komoditas_id' => ['nullable', 'exists:jenis_komoditas,id'],
|
||||||
'komoditas_kode' => ['nullable', 'string', 'max:255'],
|
'komoditas_kode' => ['nullable', 'string', 'max:255'],
|
||||||
|
'varietas_id' => ['nullable', 'numeric'],
|
||||||
|
'metode_pengumpulan' => ['nullable', 'string'],
|
||||||
|
'deskripsi' => ['nullable', 'string'],
|
||||||
|
'created_by' => ['nullable', 'numeric'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$tanaman = Tanaman::create([
|
$tanaman = Tanaman::create([
|
||||||
...$validated,
|
...$validated,
|
||||||
'profile_id' => $profile->id,
|
// 'profile_id' => $profileId,
|
||||||
'uid' => $validated['uid'] ?? $request->input('uid'),
|
// 'uid' => $validated['uid'],
|
||||||
'komoditas_kode' => $validated['komoditas_kode'] ?? $request->input('komoditas_kode'),
|
// 'komoditas_kode' => $validated['komoditas_kode'] ?? null,
|
||||||
'jenis_komoditas_id' => $validated['jenis_komoditas_id'] ?? $request->input('jenis_komoditas_id'),
|
// 'jenis_komoditas_id' => $validated['jenis_komoditas_id'] ?? null,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return (new TanamanResource($tanaman))
|
return (new TanamanResource($tanaman))
|
||||||
@@ -141,7 +141,7 @@ class TanamanController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function show(string $id): JsonResponse
|
public function show(string $id): JsonResponse
|
||||||
{
|
{
|
||||||
if (!Gate::any(['petani', 'fasilitator','super_admin'])) {
|
if (!Gate::any(['petani', 'enumerator','super_admin'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,7 +166,7 @@ class TanamanController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function update(Request $request, string $id): JsonResponse
|
public function update(Request $request, string $id): JsonResponse
|
||||||
{
|
{
|
||||||
if (!Gate::any(['petani', 'fasilitator','super_admin'])) {
|
if (!Gate::any(['petani', 'enumerator','super_admin'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
$tanaman = Tanaman::findOrFail($id);
|
$tanaman = Tanaman::findOrFail($id);
|
||||||
@@ -201,7 +201,7 @@ class TanamanController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function destroy(string $id): JsonResponse
|
public function destroy(string $id): JsonResponse
|
||||||
{
|
{
|
||||||
if (!Gate::any(['petani', 'fasilitator','super_admin'])) {
|
if (!Gate::any(['petani', 'enumerator','super_admin'])) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
$tanaman = Tanaman::findOrFail($id);
|
$tanaman = Tanaman::findOrFail($id);
|
||||||
|
|||||||
@@ -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', 'enumerator'])) {
|
||||||
|
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 === 'enumerator') {
|
||||||
|
$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', 'enumerator'])) {
|
||||||
|
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', 'enumerator'])) {
|
||||||
|
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', 'enumerator'])) {
|
||||||
|
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', 'enumerator'])) {
|
||||||
|
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', 'enumerator'])) {
|
||||||
|
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', 'enumerator'])) {
|
||||||
|
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', 'enumerator'])) {
|
||||||
|
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 === 'enumerator') {
|
||||||
|
$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,63 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Web\Concerns;
|
||||||
|
|
||||||
|
use App\Models\Pertanian\Petani;
|
||||||
|
use App\Services\Produktivitas\KelengkapanPetani;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Collection as BaseCollection;
|
||||||
|
use Illuminate\Support\Facades\Gate;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bantuan bersama menu Verifikasi > Petani (tab Monitoring & Verifikasi).
|
||||||
|
* Hanya super_admin, admin, dan koordinator; koordinator dibatasi komoditas yang ditugaskan.
|
||||||
|
*/
|
||||||
|
trait AksesMonitoringPetani
|
||||||
|
{
|
||||||
|
protected const PER_PAGE = [10, 25, 50];
|
||||||
|
|
||||||
|
protected function izinkan(): void
|
||||||
|
{
|
||||||
|
abort_unless(Gate::any(['super_admin', 'admin', 'koordinator']), 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function izinkanPetani(Petani $petani): void
|
||||||
|
{
|
||||||
|
$this->izinkan();
|
||||||
|
abort_if(auth()->user()->isKoordinator() && ! auth()->user()->bolehAksesPetani($petani), 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Query petani + relasi untuk hitung kelengkapan (eager load, tanpa N+1), sudah dibatasi komoditas koordinator. */
|
||||||
|
protected function queryPetani(): Builder
|
||||||
|
{
|
||||||
|
return $this->batasiKomoditas(Petani::query()->with(KelengkapanPetani::RELASI));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function batasiKomoditas(Builder $query): Builder
|
||||||
|
{
|
||||||
|
$user = auth()->user();
|
||||||
|
|
||||||
|
return $query->when($user->isKoordinator(), fn ($q) => $q->whereIn('komoditas_id', $user->komoditasKoordinatorIds()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Ringkasan 3 hitungan input (skor, wajib, lainnya) per petani, dikunci id petani. */
|
||||||
|
protected function ringkasan(Collection $petani): BaseCollection
|
||||||
|
{
|
||||||
|
return $petani->mapWithKeys(fn (Petani $p) => [$p->id => KelengkapanPetani::untuk($p)->ringkasanInput()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function perPage(Request $request, string $kunci): int
|
||||||
|
{
|
||||||
|
$n = (int) $request->query($kunci);
|
||||||
|
|
||||||
|
return in_array($n, self::PER_PAGE, true) ? $n : self::PER_PAGE[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function kataCari(Request $request, string $kunci): ?string
|
||||||
|
{
|
||||||
|
return $request->filled($kunci) ? Str::lower($request->query($kunci)) : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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', 'koordinator', 'enumerator'])) {
|
||||||
|
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', 'koordinator', 'enumerator'])) {
|
||||||
|
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,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Web\Dashboard;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Support\Facades\Gate;
|
||||||
|
|
||||||
|
class DistribusiController extends Controller
|
||||||
|
{
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
if (!Gate::allows('super_admin') && !Gate::allows('admin')) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
|
|
||||||
|
$title = 'Distribusi Dashboard';
|
||||||
|
|
||||||
|
return view('dashboard.distribusi', compact('title'));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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', 'koordinator', 'enumerator'])) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->route('dashboard.karakteristik-petani', array_merge($request->query(), [
|
||||||
|
'tab' => 'indeks-petani',
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function contentData(Request $request): array
|
||||||
|
{
|
||||||
|
$filters = $this->filters($request);
|
||||||
|
|
||||||
|
$segments = $this->segments();
|
||||||
|
$colors = collect($segments)
|
||||||
|
->pluck('color')
|
||||||
|
->map(fn($c) => "var(--bs-{$c})")
|
||||||
|
->values();
|
||||||
|
|
||||||
|
$komoditas = Komoditas::where('is_utama', true)->get(['id', 'nama']);
|
||||||
|
$petaniQuery = $this->petaniQuery(array_fill_keys(array_keys($filters), null));
|
||||||
|
|
||||||
|
$desaKelurahan = $petaniQuery->clone()
|
||||||
|
->selectRaw("
|
||||||
|
master_desa_kelurahan.id as desa_kelurahan_id,
|
||||||
|
master_desa_kelurahan.nama as desa_kelurahan,
|
||||||
|
master_kecamatan.id as kecamatan_id,
|
||||||
|
master_kecamatan.nama as kecamatan,
|
||||||
|
master_kabupaten_kota.id as kabupaten_id,
|
||||||
|
master_kabupaten_kota.nama as kabupaten,
|
||||||
|
master_provinsi.id as provinsi_id,
|
||||||
|
master_provinsi.nama as provinsi
|
||||||
|
")
|
||||||
|
->whereNotNull('master_provinsi.id')
|
||||||
|
->distinct()
|
||||||
|
->get();
|
||||||
|
|
||||||
|
$provinsi = $desaKelurahan
|
||||||
|
->unique('provinsi_id')
|
||||||
|
->sortBy('provinsi')
|
||||||
|
->values();
|
||||||
|
|
||||||
|
$wilayah = $desaKelurahan
|
||||||
|
->groupBy('provinsi_id')
|
||||||
|
->map(function ($provinsi) {
|
||||||
|
return [
|
||||||
|
'id' => $provinsi->first()->provinsi_id,
|
||||||
|
'nama' => $provinsi->first()->provinsi,
|
||||||
|
'kabupaten' => $provinsi
|
||||||
|
->groupBy('kabupaten_id')
|
||||||
|
->map(function ($kabupaten) {
|
||||||
|
return [
|
||||||
|
'id' => $kabupaten->first()->kabupaten_id,
|
||||||
|
'nama' => $kabupaten->first()->kabupaten,
|
||||||
|
'kecamatan' => $kabupaten
|
||||||
|
->groupBy('kecamatan_id')
|
||||||
|
->map(function ($kecamatan) {
|
||||||
|
return [
|
||||||
|
'id' => $kecamatan->first()->kecamatan_id,
|
||||||
|
'nama' => $kecamatan->first()->kecamatan,
|
||||||
|
'desa' => $kecamatan->map(function ($desa) {
|
||||||
|
return [
|
||||||
|
'id' => $desa->desa_kelurahan_id,
|
||||||
|
'nama' => $desa->desa_kelurahan,
|
||||||
|
];
|
||||||
|
})->values(),
|
||||||
|
];
|
||||||
|
})->values(),
|
||||||
|
];
|
||||||
|
})->values(),
|
||||||
|
];
|
||||||
|
})->values();
|
||||||
|
|
||||||
|
return array_merge($filters,
|
||||||
|
compact(
|
||||||
|
'komoditas',
|
||||||
|
'desaKelurahan',
|
||||||
|
'provinsi',
|
||||||
|
'wilayah',
|
||||||
|
'segments',
|
||||||
|
'colors',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function summary(Request $request): JsonResponse
|
||||||
|
{
|
||||||
|
$filters = $this->filters($request);
|
||||||
|
$petaniQuery = $this->petaniQuery($filters);
|
||||||
|
|
||||||
|
$komoditas = Komoditas::where('is_utama', true)
|
||||||
|
->when($filters['komoditas_id'], fn($q, $id) => $q->where('id', $id))->get(['id', 'nama']);
|
||||||
|
|
||||||
|
$counts = $petaniQuery->clone()
|
||||||
|
->selectRaw('COUNT(*) as petani')
|
||||||
|
->selectRaw('COUNT(DISTINCT petani.komoditas_id) FILTER (WHERE petani.komoditas_id IN (SELECT id FROM master_komoditas WHERE is_utama = true AND deleted_at IS NULL)) as komoditas')
|
||||||
|
->selectRaw('COUNT(DISTINCT petani.desa_kelurahan_id) FILTER (WHERE petani.komoditas_id IN (SELECT id FROM master_komoditas WHERE is_utama = true AND deleted_at IS NULL)) as desa')
|
||||||
|
->selectRaw('COUNT(DISTINCT master_kecamatan.id) FILTER (WHERE petani.komoditas_id IN (SELECT id FROM master_komoditas WHERE is_utama = true AND deleted_at IS NULL)) as kecamatan')
|
||||||
|
->first();
|
||||||
|
|
||||||
|
$filteredPetaniIds = $petaniQuery->clone()->select('petani.id');
|
||||||
|
$lahan = Lahan::query()
|
||||||
|
->whereIn('petani_id', $filteredPetaniIds)
|
||||||
|
->whereIn('komoditas_id', $komoditas->pluck('id'))
|
||||||
|
->selectRaw('COALESCE(SUM(luas_lahan), 0) as total, AVG(luas_lahan) as rata_rata')
|
||||||
|
->first();
|
||||||
|
|
||||||
|
$skorQuery = $this->skorPetaniQuery($filters, $komoditas->pluck('id')->toArray());
|
||||||
|
$avgSkor = $skorQuery->clone()->avg('skor_kesiapan_adopsi');
|
||||||
|
|
||||||
|
if ($avgSkor >= 4.0) {
|
||||||
|
$avgSegmen = 'Penggerak';
|
||||||
|
} elseif ($avgSkor >= 3.0) {
|
||||||
|
$avgSegmen = 'Perintis';
|
||||||
|
} elseif ($avgSkor >= 2.0) {
|
||||||
|
$avgSegmen = 'Pemerhati';
|
||||||
|
} elseif ($avgSkor >= 1.0) {
|
||||||
|
$avgSegmen = 'Pemula';
|
||||||
|
} else {
|
||||||
|
$avgSegmen = 'Belum Lengkap';
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'petani' => (int) $counts->petani,
|
||||||
|
'komoditas' => (int) $counts->komoditas,
|
||||||
|
'sum_lahan' => (float) $lahan->total,
|
||||||
|
'avg_lahan' => (float) $lahan->rata_rata,
|
||||||
|
'desa' => (int) $counts->desa,
|
||||||
|
'kecamatan' => (int) $counts->kecamatan,
|
||||||
|
'skor' => $avgSkor,
|
||||||
|
'segmen' => $avgSegmen
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function segments(): array
|
||||||
|
{
|
||||||
|
$segments = [
|
||||||
|
[
|
||||||
|
'name' => 'Penggerak',
|
||||||
|
'range' => '4.0 - 5.0',
|
||||||
|
'icon' => 'tabler-crown',
|
||||||
|
'color' => 'success',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'Perintis',
|
||||||
|
'range' => '3.0 - 3.9',
|
||||||
|
'icon' => 'tabler-flag',
|
||||||
|
'color' => 'info',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'Pemerhati',
|
||||||
|
'range' => '2.0 - 2.9',
|
||||||
|
'icon' => 'tabler-eye',
|
||||||
|
'color' => 'warning',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'Pemula',
|
||||||
|
'range' => '1.0 - 1.9',
|
||||||
|
'icon' => 'tabler-seedling',
|
||||||
|
'color' => 'danger',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
return $segments;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function cases(): string
|
||||||
|
{
|
||||||
|
$case = "
|
||||||
|
CASE
|
||||||
|
WHEN skor_kesiapan_adopsi >= 4.0 THEN 'Penggerak'
|
||||||
|
WHEN skor_kesiapan_adopsi >= 3.0 THEN 'Perintis'
|
||||||
|
WHEN skor_kesiapan_adopsi >= 2.0 THEN 'Pemerhati'
|
||||||
|
WHEN skor_kesiapan_adopsi >= 1.0 THEN 'Pemula'
|
||||||
|
ELSE 'Belum Lengkap'
|
||||||
|
END
|
||||||
|
";
|
||||||
|
|
||||||
|
return $case;
|
||||||
|
}
|
||||||
|
public function segmen(Request $request): JsonResponse
|
||||||
|
{
|
||||||
|
$filters = $this->filters($request);
|
||||||
|
$segments = $this->segments();
|
||||||
|
$case = $this->cases();
|
||||||
|
|
||||||
|
$komoditas = Komoditas::where('is_utama', true)
|
||||||
|
->when($filters['komoditas_id'], fn($q, $id) => $q->where('id', $id))->get(['id', 'nama']);
|
||||||
|
|
||||||
|
$skorQuery = $this->skorPetaniQuery($filters, $komoditas->pluck('id')->toArray());
|
||||||
|
|
||||||
|
$countSegmentasi = $skorQuery->clone()
|
||||||
|
->selectRaw("$case AS segmen, COUNT(*) AS total")
|
||||||
|
->groupByRaw($case)
|
||||||
|
->get();
|
||||||
|
|
||||||
|
$series = collect($segments)
|
||||||
|
->map(function ($segment) use ($countSegmentasi) {
|
||||||
|
return $countSegmentasi
|
||||||
|
->where('segmen', $segment['name'])
|
||||||
|
->sum('total');
|
||||||
|
})
|
||||||
|
->values();
|
||||||
|
|
||||||
|
$countPetani = $series->sum();
|
||||||
|
|
||||||
|
$persentase = collect($segments)
|
||||||
|
->map(function ($segment) use ($countSegmentasi, $countPetani) {
|
||||||
|
$total = $countSegmentasi
|
||||||
|
->where('segmen', $segment['name'])
|
||||||
|
->sum('total');
|
||||||
|
|
||||||
|
return $countPetani > 0
|
||||||
|
? ($total / $countPetani) * 100
|
||||||
|
: 0;
|
||||||
|
})
|
||||||
|
->values();
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'series' => $series,
|
||||||
|
'persentase' => $persentase,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function segmenByKomoditas(Request $request): JsonResponse
|
||||||
|
{
|
||||||
|
$filters = $this->filters($request);
|
||||||
|
$segments = $this->segments();
|
||||||
|
$case = $this->cases();
|
||||||
|
|
||||||
|
$komoditas = Komoditas::where('is_utama', true)
|
||||||
|
->when($filters['komoditas_id'], fn($q, $id) => $q->where('id', $id))->get(['id', 'nama']);
|
||||||
|
|
||||||
|
$skorQuery = $this->skorPetaniQuery($filters, $komoditas->pluck('id')->toArray());
|
||||||
|
|
||||||
|
$countSegmentasiByKomoditas = $skorQuery->clone()
|
||||||
|
->selectRaw("
|
||||||
|
petani.komoditas_id,
|
||||||
|
$case AS segmen,
|
||||||
|
COUNT(petani.id) AS total
|
||||||
|
")
|
||||||
|
->groupBy('petani.komoditas_id')
|
||||||
|
->groupByRaw($case)
|
||||||
|
->get();
|
||||||
|
|
||||||
|
$seriesSegmentasiKomoditas = collect($segments)->map(function ($segment) use ($countSegmentasiByKomoditas, $komoditas) {
|
||||||
|
return [
|
||||||
|
'name' => $segment['name'],
|
||||||
|
'data' => $komoditas->map(function ($item) use ($countSegmentasiByKomoditas, $segment) {
|
||||||
|
|
||||||
|
return optional(
|
||||||
|
$countSegmentasiByKomoditas->first(function ($row) use ($item, $segment) {
|
||||||
|
return $row->komoditas_id == $item->id
|
||||||
|
&& $row->segmen == $segment['name'];
|
||||||
|
})
|
||||||
|
)->total ?? 0;
|
||||||
|
|
||||||
|
})->values(),
|
||||||
|
];
|
||||||
|
})->values();
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'categories' => $komoditas->pluck('nama')->values(),
|
||||||
|
'series' => $seriesSegmentasiKomoditas,
|
||||||
|
]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function dimensi(Request $request): JsonResponse
|
||||||
|
{
|
||||||
|
$filters = $this->filters($request);
|
||||||
|
|
||||||
|
$komoditas = Komoditas::where('is_utama', true)
|
||||||
|
->when($filters['komoditas_id'], fn($q, $id) => $q->where('id', $id))->get(['id', 'nama']);
|
||||||
|
|
||||||
|
$skorQuery = $this->skorPetaniQuery($filters, $komoditas->pluck('id')->toArray());
|
||||||
|
|
||||||
|
$avgDimensi = $skorQuery->clone()
|
||||||
|
->selectRaw("
|
||||||
|
avg(skor_d2_sistem_praktik_budidaya) as skor_d2_sistem_praktik_budidaya,
|
||||||
|
avg(skor_d4_sosial_ekonomi) as skor_d4_sosial_ekonomi,
|
||||||
|
avg(skor_d6_rantai_nilai_akses_pasar) as skor_d6_rantai_nilai_akses_pasar")
|
||||||
|
->first();
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'radarDimensi' => [
|
||||||
|
round($avgDimensi->skor_d2_sistem_praktik_budidaya, 2),
|
||||||
|
round($avgDimensi->skor_d4_sosial_ekonomi, 2),
|
||||||
|
round($avgDimensi->skor_d6_rantai_nilai_akses_pasar, 2),
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function komponen(Request $request): JsonResponse
|
||||||
|
{
|
||||||
|
$filters = $this->filters($request);
|
||||||
|
|
||||||
|
$komoditas = Komoditas::where('is_utama', true)
|
||||||
|
->when($filters['komoditas_id'], fn($q, $id) => $q->where('id', $id))->get(['id', 'nama']);
|
||||||
|
|
||||||
|
$skorQuery = $this->skorPetaniQuery($filters, $komoditas->pluck('id')->toArray());
|
||||||
|
|
||||||
|
$avgKomponen = $skorQuery->clone()
|
||||||
|
->selectRaw("avg(skor_komp_1_d4_k3_motivasi_kapasitas_adopsi) as skor_komp_1_d4_k3_motivasi_kapasitas_adopsi,
|
||||||
|
avg(skor_komp_2_d4_k2_ekonomi_rumah_tangga) as skor_komp_2_d4_k2_ekonomi_rumah_tangga,
|
||||||
|
avg(skor_komp_3_d2_praktik_budidaya_gap) as skor_komp_3_d2_praktik_budidaya_gap,
|
||||||
|
avg(skor_komp_4_d4_k4_keterlibatan_kelembagaan) as skor_komp_4_d4_k4_keterlibatan_kelembagaan,
|
||||||
|
avg(skor_komp_5_d4_k5_k6_akses_kebebasan_keuangan) as skor_komp_5_d4_k5_k6_akses_kebebasan_keuangan,
|
||||||
|
avg(skor_komp_6_d6_k1_k4_k5_rantai_nilai_akses_pasar) as skor_komp_6_d6_k1_k4_k5_rantai_nilai_akses_pasar")
|
||||||
|
->first();
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'radarKomponen' => [
|
||||||
|
round($avgKomponen->skor_komp_1_d4_k3_motivasi_kapasitas_adopsi, 2),
|
||||||
|
round($avgKomponen->skor_komp_2_d4_k2_ekonomi_rumah_tangga, 2),
|
||||||
|
round($avgKomponen->skor_komp_3_d2_praktik_budidaya_gap, 2),
|
||||||
|
round($avgKomponen->skor_komp_4_d4_k4_keterlibatan_kelembagaan, 2),
|
||||||
|
round($avgKomponen->skor_komp_5_d4_k5_k6_akses_kebebasan_keuangan, 2),
|
||||||
|
round($avgKomponen->skor_komp_6_d6_k1_k4_k5_rantai_nilai_akses_pasar, 2)
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function villageData(array $filters, bool $includePath = true): \Illuminate\Support\Collection
|
||||||
|
{
|
||||||
|
$case = $this->cases();
|
||||||
|
|
||||||
|
$petaniQuery = $this->petaniQuery($filters);
|
||||||
|
|
||||||
|
$desaKelurahan = $petaniQuery->clone()
|
||||||
|
->selectRaw("
|
||||||
|
master_desa_kelurahan.id as desa_kelurahan_id,
|
||||||
|
master_desa_kelurahan.nama as desa_kelurahan,
|
||||||
|
master_kecamatan.id as kecamatan_id,
|
||||||
|
master_kecamatan.nama as kecamatan,
|
||||||
|
master_kabupaten_kota.id as kabupaten_id,
|
||||||
|
master_kabupaten_kota.nama as kabupaten,
|
||||||
|
master_provinsi.id as provinsi_id,
|
||||||
|
master_provinsi.nama as provinsi
|
||||||
|
")
|
||||||
|
->when($includePath, fn (Builder $query) => $query->addSelect('master_desa_kelurahan.path as path'))
|
||||||
|
->distinct()
|
||||||
|
->get();
|
||||||
|
|
||||||
|
$komoditas = Komoditas::where('is_utama', true)
|
||||||
|
->when($filters['komoditas_id'], fn($q, $id) => $q->where('id', $id))->get(['id', 'nama']);
|
||||||
|
|
||||||
|
$skorQuery = $this->skorPetaniQuery($filters, $komoditas->pluck('id')->toArray());
|
||||||
|
|
||||||
|
$mapSegmentasi = $skorQuery->clone()
|
||||||
|
->selectRaw("petani.desa_kelurahan_id,
|
||||||
|
$case AS segmen,
|
||||||
|
COUNT(petani.id) AS total
|
||||||
|
")
|
||||||
|
->groupBy('petani.desa_kelurahan_id')
|
||||||
|
->groupByRaw($case)
|
||||||
|
->get();
|
||||||
|
|
||||||
|
$mapAvgSkorSegmentasi = $skorQuery->clone()
|
||||||
|
->selectRaw("petani.desa_kelurahan_id,
|
||||||
|
COUNT(petani.id) AS jumlah_petani,
|
||||||
|
avg(skor_kesiapan_adopsi) AS skor,
|
||||||
|
CASE
|
||||||
|
WHEN AVG(skor_kesiapan_adopsi) >= 4 THEN 'Penggerak'
|
||||||
|
WHEN AVG(skor_kesiapan_adopsi) >= 3 THEN 'Perintis'
|
||||||
|
WHEN AVG(skor_kesiapan_adopsi) >= 2 THEN 'Pemerhati'
|
||||||
|
WHEN AVG(skor_kesiapan_adopsi) >= 1 THEN 'Pemula'
|
||||||
|
ELSE 'Belum Lengkap'
|
||||||
|
END AS segmen
|
||||||
|
")
|
||||||
|
->groupBy('petani.desa_kelurahan_id')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
$segmentasiGroups = $mapSegmentasi->groupBy('desa_kelurahan_id');
|
||||||
|
$jumlahSegmenByDesa = $segmentasiGroups
|
||||||
|
->map(fn ($items) => $items->pluck('total', 'segmen'));
|
||||||
|
|
||||||
|
$segmenDominan = $segmentasiGroups
|
||||||
|
->map(function ($items) {
|
||||||
|
return $items->reduce(fn ($largest, $item) => $largest === null || $item->total > $largest->total ? $item : $largest);
|
||||||
|
});
|
||||||
|
|
||||||
|
$mapAvgSkorSegmentasi->each(function ($item) use ($segmenDominan, $jumlahSegmenByDesa) {
|
||||||
|
$dominan = $segmenDominan->get($item->desa_kelurahan_id);
|
||||||
|
|
||||||
|
$item->segmen_dominan = $dominan?->segmen;
|
||||||
|
$item->jumlah_dominan = $dominan?->total;
|
||||||
|
$item->jumlah_segmen = $jumlahSegmenByDesa->get($item->desa_kelurahan_id, collect());
|
||||||
|
});
|
||||||
|
|
||||||
|
$segmentasiByDesa = $mapAvgSkorSegmentasi->keyBy('desa_kelurahan_id');
|
||||||
|
$desaLookup = $desaKelurahan->keyBy('desa_kelurahan_id');
|
||||||
|
|
||||||
|
$mapDesaSegmentasi = $segmentasiByDesa->map(function ($segmentasi, $desaId) use ($desaLookup) {
|
||||||
|
$desa = $desaLookup->get($desaId);
|
||||||
|
if (!$desa) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$desa->segmentasi = $segmentasi;
|
||||||
|
|
||||||
|
return $desa;
|
||||||
|
})->filter()->values();
|
||||||
|
|
||||||
|
return $mapDesaSegmentasi;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function map(Request $request): JsonResponse
|
||||||
|
{
|
||||||
|
return response()->json($this->villageData($this->filters($request)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function desa(Request $request): JsonResponse
|
||||||
|
{
|
||||||
|
$segmentStyles = collect($this->segments())->keyBy('name');
|
||||||
|
$mapDesaSegmentasi = $this->villageData($this->filters($request), false);
|
||||||
|
|
||||||
|
$segmentNames = ['Pemula', 'Perintis', 'Pemerhati', 'Penggerak', 'Belum Lengkap'];
|
||||||
|
$rows = $mapDesaSegmentasi->map(function ($desa) use ($segmentStyles, $segmentNames) {
|
||||||
|
$total = (int) $desa->segmentasi->jumlah_petani;
|
||||||
|
|
||||||
|
$style = $segmentStyles[$desa->segmentasi->segmen] ?? ['color' => 'secondary'];
|
||||||
|
$styleDominan = $segmentStyles[$desa->segmentasi->segmen_dominan] ?? ['color' => 'secondary'];
|
||||||
|
|
||||||
|
return [
|
||||||
|
'desa' => $desa->desa_kelurahan,
|
||||||
|
'jumlah_petani' => $desa->segmentasi->jumlah_petani,
|
||||||
|
'segmen' => $desa->segmentasi->segmen,
|
||||||
|
'skor' => $desa->segmentasi->skor,
|
||||||
|
'segmen_dominan' => $desa->segmentasi->segmen_dominan,
|
||||||
|
'jumlah_dominan' => $desa->segmentasi->jumlah_dominan,
|
||||||
|
'color' => $style['color'],
|
||||||
|
'color_dominan' => $styleDominan['color'],
|
||||||
|
'segments' => collect($segmentNames)
|
||||||
|
->mapWithKeys(function ($name) use ($desa, $segmentStyles, $total) {
|
||||||
|
$count = (int) $desa->segmentasi->jumlah_segmen->get($name, 0);
|
||||||
|
|
||||||
|
return [strtolower(str_replace(' ', '_', $name)) => [
|
||||||
|
'count' => $count,
|
||||||
|
'percentage' => $total > 0 ? round($count / $total * 100, 2) : 0,
|
||||||
|
'color' => $segmentStyles[$name]['color'] ?? 'secondary',
|
||||||
|
]];
|
||||||
|
})->all(),
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
return response()->json($rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function data(Request $request): JsonResponse
|
||||||
|
{
|
||||||
|
$filters = $this->filters($request);
|
||||||
|
$segments = $this->segments();
|
||||||
|
|
||||||
|
$segmentStyles = collect($segments)->keyBy('name');
|
||||||
|
|
||||||
|
$petaniQuery = $this->petaniQuery($filters);
|
||||||
|
|
||||||
|
$komoditas = Komoditas::where('is_utama', true)
|
||||||
|
->when($filters['komoditas_id'], fn($q, $id) => $q->where('id', $id))->get(['id', 'nama']);
|
||||||
|
|
||||||
|
$daftarPetani = $petaniQuery->clone()
|
||||||
|
->select('petani.*')
|
||||||
|
->with(['desaKelurahan', 'komoditas', 'skor'])
|
||||||
|
->withSum('lahans as total_luas_lahan', 'luas_lahan')
|
||||||
|
->whereIn('komoditas_id', $komoditas->pluck('id'))
|
||||||
|
->get();
|
||||||
|
|
||||||
|
$data = $daftarPetani
|
||||||
|
->map(function ($item) use ($segmentStyles) {
|
||||||
|
$segmen = $item->skor
|
||||||
|
? $item->segmen()
|
||||||
|
: null;
|
||||||
|
$style = $segmentStyles[$segmen]
|
||||||
|
?? ['color' => 'secondary'];
|
||||||
|
|
||||||
|
return [
|
||||||
|
'id' => $item->id,
|
||||||
|
'nama' => $item->nama,
|
||||||
|
'desa' => $item->desaKelurahan?->nama,
|
||||||
|
'desa_kelurahan_id' => $item->desa_kelurahan_id,
|
||||||
|
'komoditas' => $item->komoditas?->nama,
|
||||||
|
'luas_lahan' => (float) ($item->total_luas_lahan ?? 0),
|
||||||
|
'skor' => $item->skor
|
||||||
|
? $item->skor->skor_kesiapan_adopsi
|
||||||
|
: null,
|
||||||
|
'segmen' => $segmen,
|
||||||
|
'color' => $style['color'],
|
||||||
|
'url' => route('dashboard.profil-petani', $item->id),
|
||||||
|
];
|
||||||
|
})
|
||||||
|
->values();
|
||||||
|
|
||||||
|
return response()->json($data);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,769 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Web\Dashboard;
|
||||||
|
|
||||||
|
use App\Enums\KeparahanSerangan;
|
||||||
|
use App\Enums\PenyakitTanaman;
|
||||||
|
use App\Enums\PraktikBudidaya\PemberianPupuk;
|
||||||
|
use App\Enums\SeranganHama;
|
||||||
|
use App\Enums\SosialEkonomi\StatusKepemilikanLahanSosialEkonomi;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\Pertanian\Komoditas;
|
||||||
|
use App\Models\Pertanian\Lahan;
|
||||||
|
use App\Models\Pertanian\Petani;
|
||||||
|
use App\Models\Pertanian\PraktikBudidaya;
|
||||||
|
use App\Models\Pertanian\Produksi;
|
||||||
|
use App\Models\Pertanian\SkorPetani;
|
||||||
|
use App\Models\Pertanian\SosialEkonomi;
|
||||||
|
use App\Models\Pertanian\SurveyProduktivitas;
|
||||||
|
use App\Models\Wilayah\DesaKelurahan;
|
||||||
|
use App\Models\Wilayah\KabupatenKota;
|
||||||
|
use App\Models\Wilayah\Kecamatan;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
class IndikatorPertanianController extends Controller
|
||||||
|
{
|
||||||
|
public function index(Request $request)
|
||||||
|
{
|
||||||
|
$kabupatenOptions = $this->kabupatenOptions();
|
||||||
|
$ngadaOption = collect($kabupatenOptions)->first(
|
||||||
|
fn ($option) => str_contains(strtolower($option['label']), 'ngada')
|
||||||
|
);
|
||||||
|
$initialFilters = $this->selectedFilters($request, $ngadaOption['value'] ?? 'all');
|
||||||
|
|
||||||
|
$totalPetani = $this->applyPetaniFilters(Petani::query(), $initialFilters)->count();
|
||||||
|
$totalLuasLahan = ((float) $this->applyLahanFilters(Lahan::query(), $initialFilters)->sum('luas_lahan')) / 10000;
|
||||||
|
$rataRataLuasLahan = $totalPetani > 0
|
||||||
|
? $totalLuasLahan / $totalPetani
|
||||||
|
: 0;
|
||||||
|
$totalProduksiKg = $this->totalProduksiKg($initialFilters);
|
||||||
|
$rataRataProduksiKg = $totalPetani > 0
|
||||||
|
? $totalProduksiKg / $totalPetani
|
||||||
|
: 0;
|
||||||
|
$rataRataProduktivitas = $totalLuasLahan > 0
|
||||||
|
? $totalProduksiKg / $totalLuasLahan
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
$filterOptions = [
|
||||||
|
[
|
||||||
|
'key' => 'kabupaten',
|
||||||
|
'label' => 'Kabupaten',
|
||||||
|
'options' => $kabupatenOptions,
|
||||||
|
'icon' => 'tabler-map-pin',
|
||||||
|
'readonly' => true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'key' => 'kecamatan',
|
||||||
|
'label' => 'Kecamatan',
|
||||||
|
'options' => $this->kecamatanOptions(),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'key' => 'desa',
|
||||||
|
'label' => 'Desa',
|
||||||
|
'options' => $this->desaOptions(),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'key' => 'komoditas',
|
||||||
|
'label' => 'Komoditas',
|
||||||
|
'options' => $this->komoditasOptions(),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'key' => 'segmentasi',
|
||||||
|
'label' => 'Segmentasi',
|
||||||
|
'options' => $this->segmentasiOptions(),
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
$summaryData = [
|
||||||
|
[
|
||||||
|
'value' => number_format($totalPetani, 0, ',', '.'),
|
||||||
|
'label' => 'Petani Terdata',
|
||||||
|
'description' => 'Total petani yang masuk sistem',
|
||||||
|
'icon' => 'tabler-users',
|
||||||
|
'variant' => 'primary',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'value' => number_format($totalLuasLahan, 2, ',', '.').' ha',
|
||||||
|
'label' => 'Total Luas Lahan',
|
||||||
|
'description' => 'Rata-rata '.number_format($rataRataLuasLahan, 2, ',', '.').' ha/petani',
|
||||||
|
'icon' => 'tabler-mountain',
|
||||||
|
'variant' => 'success',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'value' => number_format($totalProduksiKg, 0, ',', '.').' kg',
|
||||||
|
'label' => 'Total Produksi (Semua Komoditas)',
|
||||||
|
'description' => 'Rata-rata '.number_format($rataRataProduksiKg, 0, ',', '.').' kg/petani',
|
||||||
|
'icon' => 'tabler-wheat',
|
||||||
|
'variant' => 'warning',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'value' => number_format($rataRataProduktivitas, 0, ',', '.').' kg/ha',
|
||||||
|
'label' => 'Rata-rata Produktivitas',
|
||||||
|
'description' => 'Semua komoditas berdasarkan total produksi dan luas lahan',
|
||||||
|
'icon' => 'tabler-chart-bar',
|
||||||
|
'variant' => 'info',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
return view('dashboard.indikator-pertanian', [
|
||||||
|
'title' => 'Indikator Pertanian',
|
||||||
|
'initialFilters' => $initialFilters,
|
||||||
|
'filterOptions' => $filterOptions,
|
||||||
|
'summaryData' => $summaryData,
|
||||||
|
'chartData' => [
|
||||||
|
'petaniPerKomoditas' => $this->petaniPerKomoditasChart($initialFilters),
|
||||||
|
'luasLahanPerKecamatan' => $this->luasLahanPerKecamatanChart($initialFilters),
|
||||||
|
'kondisiTanaman' => $this->kondisiTanamanChart($initialFilters),
|
||||||
|
'statusKepemilikanLahan' => $this->statusKepemilikanLahanChart($initialFilters),
|
||||||
|
'seranganHama' => $this->seranganHamaChart($initialFilters),
|
||||||
|
'penyakitTanaman' => $this->penyakitTanamanChart($initialFilters),
|
||||||
|
'keparahanSerangan' => $this->keparahanSeranganChart($initialFilters),
|
||||||
|
'statusHakKelolaLahan' => $this->statusHakKelolaLahanChart($initialFilters),
|
||||||
|
'praktikPemupukan' => $this->praktikPemupukanChart($initialFilters),
|
||||||
|
'produksiPerKomoditas' => $this->produksiPerKomoditasChart($initialFilters),
|
||||||
|
'produktivitasPerKomoditas' => $this->produktivitasPerKomoditasChart($initialFilters),
|
||||||
|
'luasPanenPerKomoditas' => $this->luasPanenPerKomoditasChart($initialFilters),
|
||||||
|
'statusPanen' => $this->statusPanenChart($initialFilters),
|
||||||
|
'surveyProduktivitasHa' => $this->surveyProduktivitasByUnitChart($initialFilters, 'ha_tahun'),
|
||||||
|
'surveyProduktivitasPohon' => $this->surveyProduktivitasByUnitChart($initialFilters, 'pohon_tahun'),
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function selectedFilters(Request $request, string $defaultKabupaten): array
|
||||||
|
{
|
||||||
|
$filters = [
|
||||||
|
'kabupaten' => $defaultKabupaten,
|
||||||
|
'kecamatan' => $this->validFilterValue($request->query('kecamatan'), Kecamatan::class),
|
||||||
|
'desa' => $this->validFilterValue($request->query('desa'), DesaKelurahan::class),
|
||||||
|
'komoditas' => $this->validFilterValue($request->query('komoditas'), Komoditas::class),
|
||||||
|
'segmentasi' => $this->validSegmentasi((string) $request->query('segmentasi', 'all')),
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($filters['desa'] !== 'all') {
|
||||||
|
$desa = DesaKelurahan::query()->find($filters['desa']);
|
||||||
|
|
||||||
|
if (! $desa || ($filters['kecamatan'] !== 'all' && (string) $desa->kecamatan_id !== $filters['kecamatan'])) {
|
||||||
|
$filters['desa'] = 'all';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($filters['kecamatan'] === 'all') {
|
||||||
|
$filters['desa'] = 'all';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $filters;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function validFilterValue(mixed $value, string $modelClass): string
|
||||||
|
{
|
||||||
|
if (! $value || $value === 'all') {
|
||||||
|
return 'all';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $modelClass::query()->whereKey($value)->exists()
|
||||||
|
? (string) $value
|
||||||
|
: 'all';
|
||||||
|
}
|
||||||
|
|
||||||
|
private function validSegmentasi(string $value): string
|
||||||
|
{
|
||||||
|
return in_array($value, [
|
||||||
|
'Penggerak',
|
||||||
|
'Perintis',
|
||||||
|
'Pemerhati',
|
||||||
|
'Pemula',
|
||||||
|
'Belum tersegmentasi',
|
||||||
|
'all',
|
||||||
|
], true) ? $value : 'all';
|
||||||
|
}
|
||||||
|
|
||||||
|
private function kabupatenOptions(): array
|
||||||
|
{
|
||||||
|
return $this->withAllOption(
|
||||||
|
'Semua Kabupaten',
|
||||||
|
KabupatenKota::query()
|
||||||
|
->orderBy('nama')
|
||||||
|
->get(['id', 'kode', 'nama'])
|
||||||
|
->map(fn (KabupatenKota $kabupaten) => [
|
||||||
|
'value' => (string) $kabupaten->id,
|
||||||
|
'label' => $this->kabupatenLabel($kabupaten),
|
||||||
|
])
|
||||||
|
->values()
|
||||||
|
->all()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function kecamatanOptions(): array
|
||||||
|
{
|
||||||
|
return $this->withAllOption(
|
||||||
|
'Semua Kecamatan',
|
||||||
|
Kecamatan::query()
|
||||||
|
->orderBy('nama')
|
||||||
|
->get(['id', 'nama'])
|
||||||
|
->map(fn (Kecamatan $kecamatan) => [
|
||||||
|
'value' => (string) $kecamatan->id,
|
||||||
|
'label' => $kecamatan->nama,
|
||||||
|
])
|
||||||
|
->values()
|
||||||
|
->all()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function desaOptions(): array
|
||||||
|
{
|
||||||
|
return $this->withAllOption(
|
||||||
|
'Semua Desa',
|
||||||
|
DesaKelurahan::query()
|
||||||
|
->orderBy('nama')
|
||||||
|
->get(['id', 'nama', 'kecamatan_id'])
|
||||||
|
->map(fn (DesaKelurahan $desa) => [
|
||||||
|
'value' => (string) $desa->id,
|
||||||
|
'label' => $desa->nama,
|
||||||
|
'parent' => (string) $desa->kecamatan_id,
|
||||||
|
])
|
||||||
|
->values()
|
||||||
|
->all()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function komoditasOptions(): array
|
||||||
|
{
|
||||||
|
return $this->withAllOption(
|
||||||
|
'Semua Komoditas',
|
||||||
|
Komoditas::query()
|
||||||
|
->orderBy('nama')
|
||||||
|
->get(['id', 'nama'])
|
||||||
|
->map(fn (Komoditas $komoditas) => [
|
||||||
|
'value' => (string) $komoditas->id,
|
||||||
|
'label' => $komoditas->nama,
|
||||||
|
])
|
||||||
|
->values()
|
||||||
|
->all()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function segmentasiOptions(): array
|
||||||
|
{
|
||||||
|
// Aggregate bucket counts in SQL instead of pulling every skor_kesiapan_adopsi
|
||||||
|
// value into PHP just to map/filter/unique it.
|
||||||
|
$row = SkorPetani::query()->selectRaw("
|
||||||
|
COUNT(*) FILTER (WHERE skor_kesiapan_adopsi >= 4) as penggerak,
|
||||||
|
COUNT(*) FILTER (WHERE skor_kesiapan_adopsi >= 3 AND skor_kesiapan_adopsi < 4) as perintis,
|
||||||
|
COUNT(*) FILTER (WHERE skor_kesiapan_adopsi >= 2 AND skor_kesiapan_adopsi < 3) as pemerhati,
|
||||||
|
COUNT(*) FILTER (WHERE skor_kesiapan_adopsi >= 1 AND skor_kesiapan_adopsi < 2) as pemula,
|
||||||
|
COUNT(*) FILTER (WHERE skor_kesiapan_adopsi IS NOT NULL AND skor_kesiapan_adopsi < 1) as belum_tersegmentasi
|
||||||
|
")->first();
|
||||||
|
|
||||||
|
$order = [
|
||||||
|
'Penggerak' => $row->penggerak,
|
||||||
|
'Perintis' => $row->perintis,
|
||||||
|
'Pemerhati' => $row->pemerhati,
|
||||||
|
'Pemula' => $row->pemula,
|
||||||
|
'Belum tersegmentasi' => $row->belum_tersegmentasi,
|
||||||
|
];
|
||||||
|
|
||||||
|
$segments = collect($order)
|
||||||
|
->filter(fn ($count) => $count > 0)
|
||||||
|
->keys()
|
||||||
|
->map(fn ($segment) => [
|
||||||
|
'value' => $segment,
|
||||||
|
'label' => $segment,
|
||||||
|
])
|
||||||
|
->values()
|
||||||
|
->all();
|
||||||
|
|
||||||
|
return $this->withAllOption('Semua Segmentasi', $segments);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function totalProduksiKg(array $filters): float
|
||||||
|
{
|
||||||
|
$expr = $this->kgCaseExpr('jumlah_panen', 'satuan');
|
||||||
|
|
||||||
|
return (float) ($this->applyProduksiFilters(Produksi::query(), $filters)
|
||||||
|
->whereNotNull('jumlah_panen')
|
||||||
|
->selectRaw("SUM({$expr}) as total")
|
||||||
|
->value('total') ?? 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function petaniPerKomoditasChart(array $filters): array
|
||||||
|
{
|
||||||
|
$rows = $this->applyPetaniFilters(Petani::query(), $filters)
|
||||||
|
->leftJoin('master_komoditas as k', 'k.id', '=', 'petani.komoditas_id')
|
||||||
|
->selectRaw("COALESCE(k.nama, 'Belum ditentukan') as label")
|
||||||
|
->selectRaw('COUNT(petani.id) as total')
|
||||||
|
->groupBy('k.id', 'k.nama')
|
||||||
|
->orderByDesc('total')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
return [
|
||||||
|
'labels' => $rows->pluck('label')->values()->all(),
|
||||||
|
'series' => $rows->pluck('total')->map(fn ($value) => (int) $value)->values()->all(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function luasLahanPerKecamatanChart(array $filters): array
|
||||||
|
{
|
||||||
|
$rows = $this->applyLahanFilters(Lahan::query(), $filters)
|
||||||
|
->leftJoin('master_desa_kelurahan as d', 'd.id', '=', 'map_lahan.desa_kelurahan_id')
|
||||||
|
->leftJoin('master_kecamatan as k', 'k.id', '=', 'd.kecamatan_id')
|
||||||
|
->selectRaw("COALESCE(k.nama, 'Belum ditentukan') as label")
|
||||||
|
->selectRaw('SUM(map_lahan.luas_lahan) / 10000 as total')
|
||||||
|
->groupBy('k.id', 'k.nama')
|
||||||
|
->havingRaw('SUM(map_lahan.luas_lahan) > 0')
|
||||||
|
->orderByDesc('total')
|
||||||
|
->limit(8)
|
||||||
|
->get()
|
||||||
|
->filter(fn ($row) => round((float) $row->total, 2) > 0)
|
||||||
|
->values();
|
||||||
|
|
||||||
|
return [
|
||||||
|
'labels' => $rows->pluck('label')->values()->all(),
|
||||||
|
'series' => $rows->pluck('total')->map(fn ($value) => round((float) $value, 2))->values()->all(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function kondisiTanamanChart(array $filters): array
|
||||||
|
{
|
||||||
|
$rows = $this->applyTanamanTableFilters(DB::table('map_tanaman'), $filters)
|
||||||
|
->whereNull('map_tanaman.deleted_at')
|
||||||
|
->whereNotNull('map_tanaman.kondisi_tanaman')
|
||||||
|
->selectRaw('map_tanaman.kondisi_tanaman as label')
|
||||||
|
->selectRaw('COUNT(map_tanaman.id) as total')
|
||||||
|
->groupBy('map_tanaman.kondisi_tanaman')
|
||||||
|
->orderByDesc('total')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
if ($rows->isEmpty()) {
|
||||||
|
$rows = $this->applyLahanFilters(Lahan::query(), $filters)
|
||||||
|
->whereNull('deleted_at')
|
||||||
|
->whereNotNull('kondisi_tanaman')
|
||||||
|
->selectRaw('kondisi_tanaman as label')
|
||||||
|
->selectRaw('COUNT(map_lahan.id) as total')
|
||||||
|
->groupBy('kondisi_tanaman')
|
||||||
|
->orderByDesc('total')
|
||||||
|
->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'labels' => $rows->pluck('label')->map(fn ($label) => $this->shortEnumLabel((string) $label))->values()->all(),
|
||||||
|
'series' => $rows->pluck('total')->map(fn ($value) => (int) $value)->values()->all(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function seranganHamaChart(array $filters): array
|
||||||
|
{
|
||||||
|
return $this->tanamanAttributeChart(
|
||||||
|
$filters,
|
||||||
|
'serangan_hama',
|
||||||
|
fn ($label) => $this->enumLabel(SeranganHama::class, (string) $label)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function penyakitTanamanChart(array $filters): array
|
||||||
|
{
|
||||||
|
return $this->tanamanAttributeChart(
|
||||||
|
$filters,
|
||||||
|
'penyakit_tanaman',
|
||||||
|
fn ($label) => $this->enumLabel(PenyakitTanaman::class, (string) $label)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function keparahanSeranganChart(array $filters): array
|
||||||
|
{
|
||||||
|
return $this->tanamanAttributeChart(
|
||||||
|
$filters,
|
||||||
|
'keparahan_serangan',
|
||||||
|
fn ($label) => $this->keparahanSeranganLabel((string) $label)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function statusKepemilikanLahanChart(array $filters): array
|
||||||
|
{
|
||||||
|
$rows = $this->applyLahanFilters(Lahan::query(), $filters)
|
||||||
|
->whereNotNull('status_kepemilikan')
|
||||||
|
->selectRaw('status_kepemilikan as label')
|
||||||
|
->selectRaw('COUNT(map_lahan.id) as total')
|
||||||
|
->groupBy('status_kepemilikan')
|
||||||
|
->orderByDesc('total')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
return $this->chartFromRows($rows, fn ($label) => $this->shortEnumLabel((string) $label));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function statusHakKelolaLahanChart(array $filters): array
|
||||||
|
{
|
||||||
|
$rows = $this->applySosialEkonomiFilters(SosialEkonomi::query(), $filters)
|
||||||
|
->whereNotNull('status_kepemilikan_lahan')
|
||||||
|
->selectRaw('status_kepemilikan_lahan as label')
|
||||||
|
->selectRaw('COUNT(sosial_ekonomi.id) as total')
|
||||||
|
->groupBy('status_kepemilikan_lahan')
|
||||||
|
->orderByDesc('total')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
return $this->chartFromRows(
|
||||||
|
$rows,
|
||||||
|
fn ($label) => $this->enumLabel(StatusKepemilikanLahanSosialEkonomi::class, (string) $label)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function praktikPemupukanChart(array $filters): array
|
||||||
|
{
|
||||||
|
$rows = $this->applyPraktikBudidayaFilters(PraktikBudidaya::query(), $filters)
|
||||||
|
->whereNotNull('pemberian_pupuk')
|
||||||
|
->selectRaw('pemberian_pupuk as label')
|
||||||
|
->selectRaw('COUNT(praktik_budidaya.id) as total')
|
||||||
|
->groupBy('pemberian_pupuk')
|
||||||
|
->orderBy('pemberian_pupuk')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
return $this->chartFromRows(
|
||||||
|
$rows,
|
||||||
|
fn ($label) => $this->pemberianPupukLabel((int) $label)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function produksiPerKomoditasChart(array $filters): array
|
||||||
|
{
|
||||||
|
$expr = $this->kgCaseExpr('produksi.jumlah_panen', 'produksi.satuan');
|
||||||
|
|
||||||
|
$rows = $this->applyProduksiFilters(Produksi::query(), $filters)
|
||||||
|
->leftJoin('master_komoditas as k', 'k.id', '=', 'produksi.komoditas_id')
|
||||||
|
->whereNotNull('jumlah_panen')
|
||||||
|
->selectRaw("COALESCE(k.nama, 'Belum ditentukan') as label")
|
||||||
|
->selectRaw("SUM({$expr}) as total")
|
||||||
|
->groupBy('k.id', 'k.nama')
|
||||||
|
->get()
|
||||||
|
->filter(fn ($row) => round((float) $row->total, 2) > 0)
|
||||||
|
->sortByDesc('total')
|
||||||
|
->values();
|
||||||
|
|
||||||
|
return [
|
||||||
|
'labels' => $rows->pluck('label')->values()->all(),
|
||||||
|
'series' => $rows->pluck('total')->map(fn ($value) => round((float) $value, 2))->values()->all(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function produktivitasPerKomoditasChart(array $filters): array
|
||||||
|
{
|
||||||
|
$expr = $this->kgCaseExpr('produksi.jumlah_panen', 'produksi.satuan');
|
||||||
|
|
||||||
|
$rows = $this->applyProduksiFilters(Produksi::query(), $filters)
|
||||||
|
->leftJoin('master_komoditas as k', 'k.id', '=', 'produksi.komoditas_id')
|
||||||
|
->selectRaw("COALESCE(k.nama, 'Belum ditentukan') as label")
|
||||||
|
->selectRaw('AVG(produksi.produktivitas) FILTER (WHERE produksi.produktivitas > 0) as avg_prod')
|
||||||
|
->selectRaw("SUM({$expr}) as total_kg")
|
||||||
|
->selectRaw('SUM(produksi.luas_panen) as total_luas')
|
||||||
|
->groupBy('k.id', 'k.nama')
|
||||||
|
->get()
|
||||||
|
->map(function ($row) {
|
||||||
|
$avgProd = (float) $row->avg_prod;
|
||||||
|
$totalLuas = (float) $row->total_luas;
|
||||||
|
$totalKg = (float) $row->total_kg;
|
||||||
|
|
||||||
|
$value = $avgProd > 0
|
||||||
|
? $avgProd
|
||||||
|
: ($totalLuas > 0 ? $totalKg / $totalLuas : 0);
|
||||||
|
|
||||||
|
return (object) ['label' => $row->label, 'total' => round($value, 2)];
|
||||||
|
})
|
||||||
|
->filter(fn ($row) => $row->total > 0)
|
||||||
|
->sortByDesc('total')
|
||||||
|
->values();
|
||||||
|
|
||||||
|
return [
|
||||||
|
'labels' => $rows->pluck('label')->values()->all(),
|
||||||
|
'series' => $rows->pluck('total')->values()->all(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function luasPanenPerKomoditasChart(array $filters): array
|
||||||
|
{
|
||||||
|
$rows = $this->applyProduksiFilters(Produksi::query(), $filters)
|
||||||
|
->leftJoin('master_komoditas as k', 'k.id', '=', 'produksi.komoditas_id')
|
||||||
|
->whereNotNull('luas_panen')
|
||||||
|
->selectRaw("COALESCE(k.nama, 'Belum ditentukan') as label")
|
||||||
|
->selectRaw('SUM(produksi.luas_panen) as total')
|
||||||
|
->groupBy('k.id', 'k.nama')
|
||||||
|
->havingRaw('SUM(produksi.luas_panen) > 0')
|
||||||
|
->orderByDesc('total')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
return [
|
||||||
|
'labels' => $rows->pluck('label')->values()->all(),
|
||||||
|
'series' => $rows->pluck('total')->map(fn ($value) => round((float) $value, 2))->values()->all(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function statusPanenChart(array $filters): array
|
||||||
|
{
|
||||||
|
$rows = $this->applyProduksiFilters(Produksi::query(), $filters)
|
||||||
|
->whereNotNull('status_panen')
|
||||||
|
->selectRaw("COALESCE(status_panen, 'Belum ditentukan') as label")
|
||||||
|
->selectRaw('COUNT(produksi.id) as total')
|
||||||
|
->groupBy('status_panen')
|
||||||
|
->orderByDesc('total')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
return $this->chartFromRows($rows, fn ($label) => $this->shortEnumLabel((string) $label));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function surveyProduktivitasByUnitChart(array $filters, string $unit): array
|
||||||
|
{
|
||||||
|
$rows = $this->applySurveyProduktivitasFilters(SurveyProduktivitas::query(), $filters)
|
||||||
|
->leftJoin('master_komoditas as k', 'k.id', '=', 'survey_produktivitas.komoditas_id')
|
||||||
|
->where('satuan', $unit)
|
||||||
|
->whereNotNull('kuantitas')
|
||||||
|
->selectRaw("COALESCE(k.nama, 'Belum ditentukan') as label")
|
||||||
|
->selectRaw('AVG(survey_produktivitas.kuantitas) as total')
|
||||||
|
->groupBy('k.id', 'k.nama')
|
||||||
|
->havingRaw('AVG(survey_produktivitas.kuantitas) > 0')
|
||||||
|
->orderByDesc('total')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
return [
|
||||||
|
'labels' => $rows->pluck('label')->values()->all(),
|
||||||
|
'series' => $rows->pluck('total')->map(fn ($value) => round((float) $value, 2))->values()->all(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function chartFromRows($rows, callable $labelResolver): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'labels' => $rows
|
||||||
|
->pluck('label')
|
||||||
|
->map(fn ($label) => $labelResolver($label))
|
||||||
|
->values()
|
||||||
|
->all(),
|
||||||
|
'series' => $rows
|
||||||
|
->pluck('total')
|
||||||
|
->map(fn ($value) => (int) $value)
|
||||||
|
->values()
|
||||||
|
->all(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function tanamanAttributeChart(array $filters, string $field, callable $labelResolver): array
|
||||||
|
{
|
||||||
|
$qualifiedField = 'map_tanaman.'.$field;
|
||||||
|
|
||||||
|
$rows = $this->applyTanamanTableFilters(DB::table('map_tanaman'), $filters)
|
||||||
|
->whereNull('map_tanaman.deleted_at')
|
||||||
|
->whereNotNull($qualifiedField)
|
||||||
|
->selectRaw($qualifiedField.' as label')
|
||||||
|
->selectRaw('COUNT(map_tanaman.id) as total')
|
||||||
|
->groupBy($qualifiedField)
|
||||||
|
->orderByDesc('total')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
if ($rows->isEmpty()) {
|
||||||
|
$rows = $this->applyLahanFilters(Lahan::query(), $filters)
|
||||||
|
->whereNotNull($field)
|
||||||
|
->selectRaw($field.' as label')
|
||||||
|
->selectRaw('COUNT(map_lahan.id) as total')
|
||||||
|
->groupBy($field)
|
||||||
|
->orderByDesc('total')
|
||||||
|
->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->chartFromRows($rows, $labelResolver);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function applyPetaniFilters($query, array $filters)
|
||||||
|
{
|
||||||
|
return $query
|
||||||
|
->when($filters['kecamatan'] !== 'all', function ($query) use ($filters) {
|
||||||
|
$query->whereHas('desaKelurahan', fn ($desa) => $desa->where('kecamatan_id', $filters['kecamatan']));
|
||||||
|
})
|
||||||
|
->when($filters['desa'] !== 'all', fn ($query) => $query->where('desa_kelurahan_id', $filters['desa']))
|
||||||
|
->when($filters['komoditas'] !== 'all', fn ($query) => $query->where('komoditas_id', $filters['komoditas']))
|
||||||
|
->when($filters['segmentasi'] !== 'all', function ($query) use ($filters) {
|
||||||
|
$query->whereHas('skor', function ($skor) use ($filters) {
|
||||||
|
$this->applySegmentasiRange($skor, $filters['segmentasi']);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private function applyLahanFilters($query, array $filters)
|
||||||
|
{
|
||||||
|
return $query
|
||||||
|
->when($filters['kecamatan'] !== 'all', function ($query) use ($filters) {
|
||||||
|
$query->whereHas('desaKelurahan', fn ($desa) => $desa->where('kecamatan_id', $filters['kecamatan']));
|
||||||
|
})
|
||||||
|
->when($filters['desa'] !== 'all', fn ($query) => $query->where('desa_kelurahan_id', $filters['desa']))
|
||||||
|
->when($filters['komoditas'] !== 'all', fn ($query) => $query->where('komoditas_id', $filters['komoditas']))
|
||||||
|
->when($filters['segmentasi'] !== 'all', function ($query) use ($filters) {
|
||||||
|
$query->whereIn('petani_id', $this->segmentasiPetaniIdsQuery($filters['segmentasi']));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private function applyProduksiFilters($query, array $filters)
|
||||||
|
{
|
||||||
|
return $query
|
||||||
|
->when($filters['kecamatan'] !== 'all' || $filters['desa'] !== 'all' || $filters['segmentasi'] !== 'all', function ($query) use ($filters) {
|
||||||
|
$query->whereHas('lahan', function ($lahan) use ($filters) {
|
||||||
|
$this->applyLahanFilters($lahan, $filters);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
->when($filters['komoditas'] !== 'all', fn ($query) => $query->where('komoditas_id', $filters['komoditas']));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function applySosialEkonomiFilters($query, array $filters)
|
||||||
|
{
|
||||||
|
return $query->whereHas('petani', function ($petani) use ($filters) {
|
||||||
|
$this->applyPetaniFilters($petani, $filters);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private function applyPraktikBudidayaFilters($query, array $filters)
|
||||||
|
{
|
||||||
|
return $query->whereHas('petani', function ($petani) use ($filters) {
|
||||||
|
$this->applyPetaniFilters($petani, $filters);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private function applySurveyProduktivitasFilters($query, array $filters)
|
||||||
|
{
|
||||||
|
return $query
|
||||||
|
->whereHas('petani', function ($petani) use ($filters) {
|
||||||
|
$this->applyPetaniFilters($petani, $filters);
|
||||||
|
})
|
||||||
|
->when($filters['komoditas'] !== 'all', fn ($query) => $query->where('komoditas_id', $filters['komoditas']));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function applyTanamanTableFilters($query, array $filters)
|
||||||
|
{
|
||||||
|
return $query
|
||||||
|
->leftJoin('map_lahan as l', 'l.id', '=', 'map_tanaman.lahan_id')
|
||||||
|
->when($filters['kecamatan'] !== 'all', function ($query) use ($filters) {
|
||||||
|
$query
|
||||||
|
->leftJoin('master_desa_kelurahan as d', 'd.id', '=', 'l.desa_kelurahan_id')
|
||||||
|
->where('d.kecamatan_id', $filters['kecamatan']);
|
||||||
|
})
|
||||||
|
->when($filters['desa'] !== 'all', fn ($query) => $query->where('l.desa_kelurahan_id', $filters['desa']))
|
||||||
|
->when($filters['komoditas'] !== 'all', fn ($query) => $query->where('map_tanaman.komoditas_id', $filters['komoditas']))
|
||||||
|
->when($filters['segmentasi'] !== 'all', function ($query) use ($filters) {
|
||||||
|
$query->where(function ($query) use ($filters) {
|
||||||
|
$query
|
||||||
|
->whereIn('map_tanaman.petani_id', $this->segmentasiPetaniIdsQuery($filters['segmentasi']))
|
||||||
|
->orWhereIn('l.petani_id', $this->segmentasiPetaniIdsQuery($filters['segmentasi']));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private function segmentasiPetaniIdsQuery(string $segmentasi)
|
||||||
|
{
|
||||||
|
return Petani::query()
|
||||||
|
->select('id')
|
||||||
|
->whereHas('skor', function ($skor) use ($segmentasi) {
|
||||||
|
$this->applySegmentasiRange($skor, $segmentasi);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private function applySegmentasiRange($query, string $segmentasi)
|
||||||
|
{
|
||||||
|
return match ($segmentasi) {
|
||||||
|
'Penggerak' => $query->where('skor_kesiapan_adopsi', '>=', 4),
|
||||||
|
'Perintis' => $query->where('skor_kesiapan_adopsi', '>=', 3)->where('skor_kesiapan_adopsi', '<', 4),
|
||||||
|
'Pemerhati' => $query->where('skor_kesiapan_adopsi', '>=', 2)->where('skor_kesiapan_adopsi', '<', 3),
|
||||||
|
'Pemula' => $query->where('skor_kesiapan_adopsi', '>=', 1)->where('skor_kesiapan_adopsi', '<', 2),
|
||||||
|
default => $query->where(function ($query) {
|
||||||
|
$query->whereNull('skor_kesiapan_adopsi')->orWhere('skor_kesiapan_adopsi', '<', 1);
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SQL CASE expression converting a jumlah/satuan pair to kilograms,
|
||||||
|
* so aggregation happens in the database instead of pulling every
|
||||||
|
* row of `produksi` into PHP to sum with toKilograms().
|
||||||
|
*/
|
||||||
|
private function kgCaseExpr(string $jumlahColumn, string $satuanColumn): string
|
||||||
|
{
|
||||||
|
return "CASE LOWER({$satuanColumn})
|
||||||
|
WHEN 'ton' THEN {$jumlahColumn} * 1000
|
||||||
|
WHEN 'kuintal' THEN {$jumlahColumn} * 100
|
||||||
|
WHEN 'kwintal' THEN {$jumlahColumn} * 100
|
||||||
|
ELSE {$jumlahColumn}
|
||||||
|
END";
|
||||||
|
}
|
||||||
|
|
||||||
|
private function shortEnumLabel(string $value): string
|
||||||
|
{
|
||||||
|
return str($value)
|
||||||
|
->replace('_', ' ')
|
||||||
|
->headline()
|
||||||
|
->toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function enumLabel(string $enumClass, int|string $value): string
|
||||||
|
{
|
||||||
|
$case = $enumClass::tryFrom($value);
|
||||||
|
|
||||||
|
if (! $case) {
|
||||||
|
return $this->shortEnumLabel((string) $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return method_exists($case, 'label')
|
||||||
|
? $case->label()
|
||||||
|
: $this->shortEnumLabel((string) $case->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function keparahanSeranganLabel(string $value): string
|
||||||
|
{
|
||||||
|
return match (KeparahanSerangan::tryFrom($value)) {
|
||||||
|
KeparahanSerangan::RINGAN => 'Ringan',
|
||||||
|
KeparahanSerangan::SEDANG => 'Sedang',
|
||||||
|
KeparahanSerangan::BERAT => 'Parah',
|
||||||
|
default => $this->shortEnumLabel($value),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private function pemberianPupukLabel(int $value): string
|
||||||
|
{
|
||||||
|
return match (PemberianPupuk::tryFrom($value)) {
|
||||||
|
PemberianPupuk::TIDAK_PERNAH => 'Tidak pernah',
|
||||||
|
PemberianPupuk::JENIS_DOSIS_WAKTU_TIDAK_MENENTU => 'Tidak menentu',
|
||||||
|
PemberianPupuk::JENIS_SESUAI_DOSIS_TIDAK_MENENTU => 'Jenis sesuai',
|
||||||
|
PemberianPupuk::JENIS_DOSIS_SESUAI_WAKTU_CUKUP_TEPAT => 'Cukup tepat',
|
||||||
|
PemberianPupuk::JENIS_DOSIS_SESUAI_WAKTU_TEPAT => 'Tepat & rutin',
|
||||||
|
default => $this->shortEnumLabel((string) $value),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private function toKilograms(float $value, string $unit): float
|
||||||
|
{
|
||||||
|
return match (strtolower($unit)) {
|
||||||
|
'ton' => $value * 1000,
|
||||||
|
'kuintal', 'kwintal' => $value * 100,
|
||||||
|
default => $value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private function withAllOption(string $label, array $options): array
|
||||||
|
{
|
||||||
|
return array_merge([
|
||||||
|
[
|
||||||
|
'value' => 'all',
|
||||||
|
'label' => $label,
|
||||||
|
],
|
||||||
|
], $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function kabupatenLabel(KabupatenKota $kabupaten): string
|
||||||
|
{
|
||||||
|
return preg_replace('/^(kabupaten|kota)\s+/i', '', $kabupaten->nama);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function segmentLabel(float $score): string
|
||||||
|
{
|
||||||
|
return match (true) {
|
||||||
|
$score >= 4.0 => 'Penggerak',
|
||||||
|
$score >= 3.0 => 'Perintis',
|
||||||
|
$score >= 2.0 => 'Pemerhati',
|
||||||
|
$score >= 1.0 => 'Pemula',
|
||||||
|
default => 'Belum tersegmentasi',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,210 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Web\Dashboard;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Services\AlphaVantageMarket;
|
||||||
|
use Illuminate\Contracts\Cache\Repository;
|
||||||
|
use Illuminate\Http\Client\Pool;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use Illuminate\Support\Facades\Gate;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
class InternationalMarketController extends Controller
|
||||||
|
{
|
||||||
|
public function __invoke()
|
||||||
|
{
|
||||||
|
abort_unless(Gate::allows('super_admin') || Gate::allows('admin') || Gate::allows('koordinator') || Gate::allows('enumerator'), 403);
|
||||||
|
|
||||||
|
// File cache keeps external quotes independent of the application database.
|
||||||
|
$cache = Cache::store('file');
|
||||||
|
$marketData = $cache->remember('market.yahoo.quotes.v3', 300, function () use ($cache) {
|
||||||
|
$commodities = [
|
||||||
|
['name' => 'Kopi Arabica', 'symbol' => 'KC=F'],
|
||||||
|
['name' => 'Kakao (Cocoa)', 'symbol' => 'CC=F'],
|
||||||
|
];
|
||||||
|
|
||||||
|
try {
|
||||||
|
$responses = Http::pool(function (Pool $pool) use ($commodities) {
|
||||||
|
return array_map(fn ($commodity) => $pool->as($commodity['symbol'])
|
||||||
|
->acceptJson()->withHeaders(['User-Agent' => 'Mozilla/5.0'])
|
||||||
|
->withOptions(['verify' => config('services.yahoo_finance.ca_bundle') ?: true])
|
||||||
|
->connectTimeout(3)->timeout(8)
|
||||||
|
->get('https://query1.finance.yahoo.com/v8/finance/chart/'.rawurlencode($commodity['symbol']), [
|
||||||
|
'interval' => '1d', 'range' => '5d',
|
||||||
|
]), [...$commodities, ['symbol' => 'IDR=X']]);
|
||||||
|
});
|
||||||
|
} catch (Throwable $exception) {
|
||||||
|
report($exception);
|
||||||
|
$responses = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$exchangeRate = $cache->get('market.yahoo.usd_idr');
|
||||||
|
if (is_array($exchangeRate)) {
|
||||||
|
$exchangeRate['stale'] = true;
|
||||||
|
}
|
||||||
|
$rateResponse = $responses['IDR=X'] ?? null;
|
||||||
|
if ($rateResponse instanceof \Illuminate\Http\Client\Response && $rateResponse->successful()) {
|
||||||
|
$meta = $rateResponse->json('chart.result.0.meta');
|
||||||
|
if (is_array($meta) && ($meta['symbol'] ?? null) === 'IDR=X'
|
||||||
|
&& ($meta['currency'] ?? null) === 'IDR'
|
||||||
|
&& is_numeric($meta['regularMarketPrice'] ?? null) && $meta['regularMarketPrice'] > 0
|
||||||
|
&& is_numeric($meta['regularMarketTime'] ?? null)) {
|
||||||
|
$exchangeRate = [
|
||||||
|
'value' => (float) $meta['regularMarketPrice'],
|
||||||
|
'quoted_at' => gmdate('c', (int) $meta['regularMarketTime']),
|
||||||
|
'source' => 'Yahoo Finance',
|
||||||
|
'source_url' => 'https://finance.yahoo.com/quote/IDR%3DX/',
|
||||||
|
'stale' => false,
|
||||||
|
];
|
||||||
|
$cache->put('market.yahoo.usd_idr', $exchangeRate, now()->addDays(7));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$quotes = array_map(function ($commodity) use ($responses, $cache) {
|
||||||
|
$symbol = $commodity['symbol'];
|
||||||
|
$key = 'market.yahoo.last.'.rawurlencode($symbol);
|
||||||
|
$quote = $commodity + [
|
||||||
|
'source' => 'Yahoo Finance',
|
||||||
|
'source_url' => 'https://finance.yahoo.com/quote/'.rawurlencode($symbol).'/',
|
||||||
|
'price' => null, 'currency' => null, 'unit' => null,
|
||||||
|
'quoted_at' => null, 'stale' => false,
|
||||||
|
];
|
||||||
|
|
||||||
|
try {
|
||||||
|
$response = $responses[$symbol] ?? null;
|
||||||
|
if (!$response instanceof \Illuminate\Http\Client\Response || !$response->successful()) {
|
||||||
|
throw new \RuntimeException('Quote unavailable');
|
||||||
|
}
|
||||||
|
$meta = $response->json('chart.result.0.meta');
|
||||||
|
if ($response->json('chart.error') || !is_array($meta)
|
||||||
|
|| ($meta['symbol'] ?? null) !== $symbol
|
||||||
|
|| !is_numeric($meta['regularMarketPrice'] ?? null)
|
||||||
|
|| (float) $meta['regularMarketPrice'] <= 0
|
||||||
|
|| !is_numeric($meta['regularMarketTime'] ?? null)
|
||||||
|
|| !is_string($meta['currency'] ?? null)) {
|
||||||
|
throw new \RuntimeException('Invalid quote');
|
||||||
|
}
|
||||||
|
|
||||||
|
$quote['price'] = (float) $meta['regularMarketPrice'];
|
||||||
|
$quote['currency'] = $meta['currency'];
|
||||||
|
// Yahoo quotes Arabica in US cents/lb; cocoa and robusta in USD/tonne.
|
||||||
|
// Apply the unit only when the API currency matches that convention.
|
||||||
|
$quote['unit'] = match (true) {
|
||||||
|
$symbol === 'KC=F' && $meta['currency'] === 'USX' => 'sen AS / lb',
|
||||||
|
$symbol === 'CC=F' && $meta['currency'] === 'USD' => 'USD / ton metrik',
|
||||||
|
default => $meta['currency'].' (satuan mengikuti sumber)',
|
||||||
|
};
|
||||||
|
$quote['quoted_at'] = gmdate('c', (int) $meta['regularMarketTime']);
|
||||||
|
$cache->put($key, $quote, now()->addDays(7));
|
||||||
|
|
||||||
|
return $quote;
|
||||||
|
} catch (Throwable $exception) {
|
||||||
|
$previous = $cache->get($key);
|
||||||
|
if (is_array($previous)) {
|
||||||
|
$previous['stale'] = true;
|
||||||
|
return $previous;
|
||||||
|
}
|
||||||
|
return $quote;
|
||||||
|
}
|
||||||
|
}, $commodities);
|
||||||
|
|
||||||
|
return ['quotes' => $quotes, 'exchange_rate' => $exchangeRate];
|
||||||
|
});
|
||||||
|
|
||||||
|
$quotes = $marketData['quotes'];
|
||||||
|
$exchangeRate = $marketData['exchange_rate'];
|
||||||
|
foreach ($quotes as &$quote) {
|
||||||
|
$quote['id'] = 'yahoo:'.$quote['symbol'];
|
||||||
|
$quote['description'] = 'Estimasi konversi harga kontrak berjangka internasional.';
|
||||||
|
$quote['period'] = $quote['quoted_at'];
|
||||||
|
$quote['frequency'] = 'market';
|
||||||
|
}
|
||||||
|
unset($quote);
|
||||||
|
$quotes = array_merge($quotes, $this->imfQuotes($cache), app(AlphaVantageMarket::class)->quotes($cache));
|
||||||
|
$quotes = array_values(array_filter($quotes, fn ($quote) => is_numeric($quote['price']) && is_finite((float) $quote['price']) && $quote['price'] > 0));
|
||||||
|
foreach ($quotes as &$quote) {
|
||||||
|
// All active providers quote either US cents/lb or USD/metric tonne.
|
||||||
|
$usdPerKg = match ($quote['unit']) {
|
||||||
|
'sen AS / lb' => $quote['price'] / 100 / 0.45359237,
|
||||||
|
'USD / ton metrik' => $quote['price'] / 1000,
|
||||||
|
default => null,
|
||||||
|
};
|
||||||
|
$quote['price_idr_per_kg'] = $exchangeRate && $usdPerKg !== null
|
||||||
|
? round($usdPerKg * $exchangeRate['value'], 2) : null;
|
||||||
|
unset($quote['usd_per_kg']); // Old Alpha cache entries may contain this field.
|
||||||
|
}
|
||||||
|
unset($quote);
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'quotes' => $quotes,
|
||||||
|
'exchange_rate' => $exchangeRate,
|
||||||
|
])->header('Cache-Control', 'no-store');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function imfQuotes(Repository $cache): array
|
||||||
|
{
|
||||||
|
// IMF observations are monthly averages; fetch at most once a day.
|
||||||
|
return $cache->remember('market.imf.monthly.v1', 86400, function () use ($cache) {
|
||||||
|
try {
|
||||||
|
$response = Http::withHeaders(['Accept' => 'text/csv', 'User-Agent' => 'Mozilla/5.0'])
|
||||||
|
->withOptions(['verify' => config('services.yahoo_finance.ca_bundle') ?: true])
|
||||||
|
->connectTimeout(3)->timeout(10)
|
||||||
|
->get('https://api.imf.org/external/sdmx/2.1/data/IMF.RES,PCPS/G001.PCOFFOTM+PCOFFROB+PCOCO.USD.M', [
|
||||||
|
'startPeriod' => now()->subYears(2)->format('Y-m'), 'format' => 'csvfile',
|
||||||
|
])->throw();
|
||||||
|
$stream = fopen('php://memory', 'r+');
|
||||||
|
try {
|
||||||
|
fwrite($stream, $response->body());
|
||||||
|
rewind($stream);
|
||||||
|
$headers = fgetcsv($stream, 0, ',', '"', '');
|
||||||
|
if (!$headers || array_diff(['COUNTRY', 'INDICATOR', 'DATA_TRANSFORMATION', 'FREQUENCY', 'TIME_PERIOD', 'OBS_VALUE', 'SCALE'], $headers)) {
|
||||||
|
throw new \RuntimeException('Unexpected IMF CSV format');
|
||||||
|
}
|
||||||
|
$latest = [];
|
||||||
|
while (($values = fgetcsv($stream, 0, ',', '"', '')) !== false) {
|
||||||
|
if (count($values) !== count($headers)) continue;
|
||||||
|
$row = array_combine($headers, $values);
|
||||||
|
if ($row['COUNTRY'] !== 'G001' || $row['DATA_TRANSFORMATION'] !== 'USD' || $row['FREQUENCY'] !== 'M'
|
||||||
|
|| $row['SCALE'] !== '0' || !is_numeric($row['OBS_VALUE']) || $row['OBS_VALUE'] <= 0
|
||||||
|
|| !preg_match('/^\d{4}-M(0[1-9]|1[0-2])$/', $row['TIME_PERIOD'])) continue;
|
||||||
|
$indicator = $row['INDICATOR'];
|
||||||
|
if (!isset($latest[$indicator]) || $row['TIME_PERIOD'] > $latest[$indicator]['TIME_PERIOD']) {
|
||||||
|
$latest[$indicator] = $row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
fclose($stream);
|
||||||
|
}
|
||||||
|
$definitions = [
|
||||||
|
'PCOFFOTM' => ['Kopi Arabica', 'USX', 'sen AS / lb', 'Biji kopi Arabica (Other Milds). Estimasi konversi harga acuan rata-rata bulanan IMF.'],
|
||||||
|
'PCOFFROB' => ['Kopi Robusta', 'USX', 'sen AS / lb', 'Biji kopi Robusta. Estimasi konversi harga acuan rata-rata bulanan IMF.'],
|
||||||
|
'PCOCO' => ['Kakao (Cocoa)', 'USD', 'USD / ton metrik', 'Biji kakao. Estimasi konversi harga acuan rata-rata bulanan IMF.'],
|
||||||
|
];
|
||||||
|
$quotes = [];
|
||||||
|
foreach ($definitions as $indicator => [$name, $currency, $unit, $description]) {
|
||||||
|
if (!isset($latest[$indicator])) continue;
|
||||||
|
$row = $latest[$indicator];
|
||||||
|
$quotes[] = [
|
||||||
|
'id' => 'imf:'.$indicator, 'symbol' => $indicator, 'name' => $name,
|
||||||
|
'price' => (float) $row['OBS_VALUE'], 'currency' => $currency, 'unit' => $unit,
|
||||||
|
'source' => 'IMF · PCPS', 'source_url' => 'https://data.imf.org/Datasets/PCPS',
|
||||||
|
'description' => $description, 'frequency' => 'monthly',
|
||||||
|
'period' => str_replace('-M', '-', $row['TIME_PERIOD']), 'stale' => false,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if (!$quotes) throw new \RuntimeException('No valid IMF observations');
|
||||||
|
$cache->put('market.imf.last.v1', $quotes, now()->addDays(30));
|
||||||
|
return $quotes;
|
||||||
|
} catch (Throwable $exception) {
|
||||||
|
report($exception);
|
||||||
|
return array_map(function ($quote) {
|
||||||
|
$quote['stale'] = true;
|
||||||
|
return $quote;
|
||||||
|
}, $cache->get('market.imf.last.v1', []));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,173 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Web\Dashboard;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Gate;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
use App\Models\Pertanian\Petani;
|
||||||
|
use App\Models\Pertanian\Komoditas;
|
||||||
|
use App\Models\Pertanian\Lahan;
|
||||||
|
use App\Models\Pertanian\Tanaman;
|
||||||
|
use App\Models\Pertanian\SkorPetani;
|
||||||
|
|
||||||
|
class ProfilPetaniController extends Controller
|
||||||
|
{
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
if (!Gate::any(['super_admin', 'admin', 'koordinator', 'enumerator'])) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
|
$title = 'Dashboard Profil Petani';
|
||||||
|
|
||||||
|
$komoditas = Komoditas::select('id', 'nama')->where('is_utama', true)->get();
|
||||||
|
$komoditasIds = $komoditas->pluck('id');
|
||||||
|
|
||||||
|
$petaniIds = fn () => Petani::query()->select('id');
|
||||||
|
$lahanIds = fn () => Lahan::query()
|
||||||
|
->whereIn('petani_id', $petaniIds())
|
||||||
|
->select('id');
|
||||||
|
|
||||||
|
$petaniSummary = Petani::select(
|
||||||
|
'komoditas_id',
|
||||||
|
DB::raw('COUNT(*) as total')
|
||||||
|
)
|
||||||
|
->groupBy('komoditas_id')
|
||||||
|
->get();
|
||||||
|
$lahanSummary = Lahan::select(
|
||||||
|
'komoditas_id',
|
||||||
|
DB::raw('COUNT(*) as total'),
|
||||||
|
DB::raw('SUM(luas_lahan) as luas_lahan')
|
||||||
|
)
|
||||||
|
->whereIn('petani_id', $petaniIds())
|
||||||
|
->groupBy('komoditas_id')
|
||||||
|
->get();
|
||||||
|
$tanamanSummary = Tanaman::select(
|
||||||
|
'komoditas_id',
|
||||||
|
DB::raw('COUNT(*) as total')
|
||||||
|
)
|
||||||
|
->whereIn('lahan_id', $lahanIds())
|
||||||
|
->groupBy('komoditas_id')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
$case = "
|
||||||
|
CASE
|
||||||
|
WHEN skor_kesiapan_adopsi >= 4.0 THEN 'Penggerak'
|
||||||
|
WHEN skor_kesiapan_adopsi >= 3.0 THEN 'Perintis'
|
||||||
|
WHEN skor_kesiapan_adopsi >= 2.0 THEN 'Pemerhati'
|
||||||
|
WHEN skor_kesiapan_adopsi >= 1.0 THEN 'Pemula'
|
||||||
|
ELSE 'Belum Lengkap'
|
||||||
|
END
|
||||||
|
";
|
||||||
|
$skorSummary = SkorPetani::selectRaw("$case AS segmen, COUNT(*) AS total")
|
||||||
|
->whereIn('petani_id', $petaniIds())
|
||||||
|
->groupByRaw($case)
|
||||||
|
->get();
|
||||||
|
|
||||||
|
$skorKomoditasSummary = SkorPetani::query()
|
||||||
|
->join('petani', 'petani_skor.petani_id', '=', 'petani.id')
|
||||||
|
->selectRaw("
|
||||||
|
petani.komoditas_id,
|
||||||
|
$case AS segmen,
|
||||||
|
COUNT(petani.id) AS total
|
||||||
|
")
|
||||||
|
->whereIn('petani_skor.petani_id', $petaniIds())
|
||||||
|
->groupBy('petani.komoditas_id')
|
||||||
|
->groupByRaw($case)
|
||||||
|
->get();
|
||||||
|
|
||||||
|
$skorAverage = SkorPetani::query()
|
||||||
|
->join('petani', 'petani_skor.petani_id', '=', 'petani.id')
|
||||||
|
->whereIn('petani_id', $petaniIds())
|
||||||
|
->whereIn('petani.komoditas_id', $komoditasIds)
|
||||||
|
->avg('skor_kesiapan_adopsi');
|
||||||
|
if ($skorAverage >= 4.0) {
|
||||||
|
$segmenAverage = 'Penggerak';
|
||||||
|
} elseif ($skorAverage >= 3.0) {
|
||||||
|
$segmenAverage = 'Perintis';
|
||||||
|
} elseif ($skorAverage >= 2.0) {
|
||||||
|
$segmenAverage = 'Pemerhati';
|
||||||
|
} elseif ($skorAverage >= 1.0) {
|
||||||
|
$segmenAverage = 'Pemula';
|
||||||
|
} else {
|
||||||
|
$segmenAverage = 'Belum Lengkap';
|
||||||
|
}
|
||||||
|
|
||||||
|
$skorDimensi = SkorPetani::query()
|
||||||
|
->join('petani', 'petani_skor.petani_id', '=', 'petani.id')
|
||||||
|
->whereIn('petani_id', $petaniIds())
|
||||||
|
->whereIn('petani.komoditas_id', $komoditasIds)
|
||||||
|
->selectRaw("
|
||||||
|
avg(skor_d2_sistem_praktik_budidaya) as skor_d2_sistem_praktik_budidaya,
|
||||||
|
avg(skor_d4_sosial_ekonomi) as skor_d4_sosial_ekonomi,
|
||||||
|
avg(skor_d6_rantai_nilai_akses_pasar) as skor_d6_rantai_nilai_akses_pasar")
|
||||||
|
->first();
|
||||||
|
|
||||||
|
$skorKomponen = SkorPetani::query()
|
||||||
|
->join('petani', 'petani_skor.petani_id', '=', 'petani.id')
|
||||||
|
->whereIn('petani_id', $petaniIds())
|
||||||
|
->whereIn('petani.komoditas_id', $komoditasIds)
|
||||||
|
->selectRaw("avg(skor_komp_1_d4_k3_motivasi_kapasitas_adopsi) as skor_komp_1_d4_k3_motivasi_kapasitas_adopsi,
|
||||||
|
avg(skor_komp_2_d4_k2_ekonomi_rumah_tangga) as skor_komp_2_d4_k2_ekonomi_rumah_tangga,
|
||||||
|
avg(skor_komp_3_d2_praktik_budidaya_gap) as skor_komp_3_d2_praktik_budidaya_gap,
|
||||||
|
avg(skor_komp_4_d4_k4_keterlibatan_kelembagaan) as skor_komp_4_d4_k4_keterlibatan_kelembagaan,
|
||||||
|
avg(skor_komp_5_d4_k5_k6_akses_kebebasan_keuangan) as skor_komp_5_d4_k5_k6_akses_kebebasan_keuangan,
|
||||||
|
avg(skor_komp_6_d6_k1_k4_k5_rantai_nilai_akses_pasar) as skor_komp_6_d6_k1_k4_k5_rantai_nilai_akses_pasar")
|
||||||
|
->first();
|
||||||
|
|
||||||
|
$skorMultiDimensi = SkorPetani::query()
|
||||||
|
->join('petani', 'petani_skor.petani_id', '=', 'petani.id')
|
||||||
|
->whereIn('petani_id', $petaniIds())
|
||||||
|
->whereIn('petani.komoditas_id', $komoditasIds)
|
||||||
|
->selectRaw("$case AS segmen,
|
||||||
|
avg(skor_kesiapan_adopsi) as skor_kesiapan_adopsi,
|
||||||
|
avg(skor_d2_sistem_praktik_budidaya) as skor_d2_sistem_praktik_budidaya,
|
||||||
|
avg(skor_d4_sosial_ekonomi) as skor_d4_sosial_ekonomi,
|
||||||
|
avg(skor_d6_rantai_nilai_akses_pasar) as skor_d6_rantai_nilai_akses_pasar")
|
||||||
|
->groupByRaw($case)
|
||||||
|
->get();
|
||||||
|
|
||||||
|
$skorMultiKomponen = SkorPetani::query()
|
||||||
|
->join('petani', 'petani_skor.petani_id', '=', 'petani.id')
|
||||||
|
->whereIn('petani_id', $petaniIds())
|
||||||
|
->whereIn('petani.komoditas_id', $komoditasIds)
|
||||||
|
->selectRaw("$case AS segmen,
|
||||||
|
avg(skor_kesiapan_adopsi) as skor_kesiapan_adopsi,
|
||||||
|
avg(skor_komp_1_d4_k3_motivasi_kapasitas_adopsi) as skor_komp_1_d4_k3_motivasi_kapasitas_adopsi,
|
||||||
|
avg(skor_komp_2_d4_k2_ekonomi_rumah_tangga) as skor_komp_2_d4_k2_ekonomi_rumah_tangga,
|
||||||
|
avg(skor_komp_3_d2_praktik_budidaya_gap) as skor_komp_3_d2_praktik_budidaya_gap,
|
||||||
|
avg(skor_komp_4_d4_k4_keterlibatan_kelembagaan) as skor_komp_4_d4_k4_keterlibatan_kelembagaan,
|
||||||
|
avg(skor_komp_5_d4_k5_k6_akses_kebebasan_keuangan) as skor_komp_5_d4_k5_k6_akses_kebebasan_keuangan,
|
||||||
|
avg(skor_komp_6_d6_k1_k4_k5_rantai_nilai_akses_pasar) as skor_komp_6_d6_k1_k4_k5_rantai_nilai_akses_pasar")
|
||||||
|
->groupByRaw($case)
|
||||||
|
->get();
|
||||||
|
|
||||||
|
$petani = Petani::query()
|
||||||
|
->select('id', 'nama', 'jenis_kelamin', 'tanggal_lahir', 'komoditas_id')
|
||||||
|
->with([
|
||||||
|
'komoditas:id,nama',
|
||||||
|
'skor:id,petani_id,skor_kesiapan_adopsi,skor_d2_sistem_praktik_budidaya,skor_d4_sosial_ekonomi,skor_d6_rantai_nilai_akses_pasar,skor_komp_1_d4_k3_motivasi_kapasitas_adopsi,skor_komp_2_d4_k2_ekonomi_rumah_tangga,skor_komp_3_d2_praktik_budidaya_gap,skor_komp_4_d4_k4_keterlibatan_kelembagaan,skor_komp_5_d4_k5_k6_akses_kebebasan_keuangan,skor_komp_6_d6_k1_k4_k5_rantai_nilai_akses_pasar',
|
||||||
|
])
|
||||||
|
->whereIn('komoditas_id', $komoditasIds)
|
||||||
|
->orderBy('nama')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
return view('dashboard.petani', compact('title',
|
||||||
|
'komoditas',
|
||||||
|
'petaniSummary',
|
||||||
|
'lahanSummary',
|
||||||
|
'tanamanSummary',
|
||||||
|
'skorSummary',
|
||||||
|
'skorKomoditasSummary',
|
||||||
|
'segmenAverage',
|
||||||
|
'skorAverage',
|
||||||
|
'skorDimensi',
|
||||||
|
'skorKomponen',
|
||||||
|
'skorMultiDimensi',
|
||||||
|
'skorMultiKomponen',
|
||||||
|
'petani',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user