diff --git a/AISTANDAR.md b/AISTANDAR.md index 444fe81..8534275 100644 --- a/AISTANDAR.md +++ b/AISTANDAR.md @@ -24,7 +24,7 @@ Baca seluruhnya sebelum membuat, mengubah, atau merefaktor kode. | Auth web | Session | | Auth API | Laravel Sanctum (token) | | Dokumentasi API | dedoc/scramble | -| Role | `app/Enums/Role.php` (atau lokasi Role yang sudah ada): `super_admin`, `admin`, `fasilitator`, `petani`, `offtaker` | +| Role | `app/Enums/Role.php`: `super_admin`, `admin` (admin program), `koordinator` (per komoditas), `enumerator` (penginput data petani), `fasilitator` (pemateri workshop), `petani`, `offtaker` | --- @@ -452,7 +452,7 @@ Aturan turunannya: - 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: `fasilitator` → Dashboard Survei Petani; role lain → Dashboard Karakteristik Petani. Jangan mengubah logika ini tanpa izin. +- Setelah login: `enumerator` → Dashboard Survei Petani; `fasilitator` → Profil akun (belum ada menu khusus); role lain → Dashboard Karakteristik Petani. Jangan mengubah logika ini tanpa izin. --- @@ -544,6 +544,8 @@ Kunci flash: `success`, `error`, `warning`, `info`. ## 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. --- diff --git a/app/Enums/Role.php b/app/Enums/Role.php index b2c7443..f911515 100644 --- a/app/Enums/Role.php +++ b/app/Enums/Role.php @@ -2,11 +2,26 @@ namespace App\Enums; -enum Role: string +enum Role: string { case SUPER_ADMIN = 'super_admin'; 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 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', + }; + } } diff --git a/app/Http/Controllers/Api/Ekosistem/FormBasisDataController.php b/app/Http/Controllers/Api/Ekosistem/FormBasisDataController.php index 2aca1c6..cfbd788 100644 --- a/app/Http/Controllers/Api/Ekosistem/FormBasisDataController.php +++ b/app/Http/Controllers/Api/Ekosistem/FormBasisDataController.php @@ -20,7 +20,7 @@ class FormBasisDataController extends Controller */ public function index(Request $request, string $tabelId): JsonResponse { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'enumerator'])) { abort(403); } diff --git a/app/Http/Controllers/Api/Ekosistem/TabelBasisDataController.php b/app/Http/Controllers/Api/Ekosistem/TabelBasisDataController.php index f02ee4f..09eaa4e 100644 --- a/app/Http/Controllers/Api/Ekosistem/TabelBasisDataController.php +++ b/app/Http/Controllers/Api/Ekosistem/TabelBasisDataController.php @@ -20,7 +20,7 @@ class TabelBasisDataController extends Controller */ public function index(Request $request): AnonymousResourceCollection { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'enumerator'])) { abort(403); } @@ -50,7 +50,7 @@ class TabelBasisDataController extends Controller */ public function show(Request $request, string $id): JsonResponse { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'enumerator'])) { abort(403); } diff --git a/app/Http/Controllers/Api/Ekosistem/ValueBasisDataController.php b/app/Http/Controllers/Api/Ekosistem/ValueBasisDataController.php index 0e3f769..6956a05 100644 --- a/app/Http/Controllers/Api/Ekosistem/ValueBasisDataController.php +++ b/app/Http/Controllers/Api/Ekosistem/ValueBasisDataController.php @@ -19,7 +19,7 @@ class ValueBasisDataController extends Controller public function index(Request $request, string $tabelId): JsonResponse { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'enumerator'])) { abort(403); } @@ -213,7 +213,7 @@ class ValueBasisDataController extends Controller public function store(Request $request, string $tabelId): JsonResponse { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'enumerator'])) { abort(403); } @@ -278,7 +278,7 @@ class ValueBasisDataController extends Controller 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); } @@ -354,7 +354,7 @@ class ValueBasisDataController extends Controller 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); } diff --git a/app/Http/Controllers/Api/Master/JenisKomoditasController.php b/app/Http/Controllers/Api/Master/JenisKomoditasController.php index edf60b7..ae24014 100644 --- a/app/Http/Controllers/Api/Master/JenisKomoditasController.php +++ b/app/Http/Controllers/Api/Master/JenisKomoditasController.php @@ -20,7 +20,7 @@ class JenisKomoditasController extends Controller */ public function index(Request $request): AnonymousResourceCollection { - if (!Gate::any(['petani', 'fasilitator', 'super_admin'])) { + if (!Gate::any(['petani', 'enumerator', 'super_admin'])) { abort(403); } @@ -63,7 +63,7 @@ class JenisKomoditasController extends Controller */ public function store(Request $request): JsonResponse { - if (!Gate::any(['petani', 'fasilitator', 'super_admin'])) { + if (!Gate::any(['petani', 'enumerator', 'super_admin'])) { abort(403); } @@ -85,7 +85,7 @@ class JenisKomoditasController extends Controller */ public function show(string $id): JsonResponse { - if (!Gate::any(['petani', 'fasilitator', 'super_admin'])) { + if (!Gate::any(['petani', 'enumerator', 'super_admin'])) { abort(403); } @@ -107,7 +107,7 @@ class JenisKomoditasController extends Controller */ public function update(Request $request, string $id): JsonResponse { - if (!Gate::any(['petani', 'fasilitator', 'super_admin'])) { + if (!Gate::any(['petani', 'enumerator', 'super_admin'])) { abort(403); } @@ -129,7 +129,7 @@ class JenisKomoditasController extends Controller */ public function destroy(string $id): JsonResponse { - if (!Gate::any(['petani', 'fasilitator', 'super_admin'])) { + if (!Gate::any(['petani', 'enumerator', 'super_admin'])) { abort(403); } diff --git a/app/Http/Controllers/Api/Produktivitas/DistributionController.php b/app/Http/Controllers/Api/Produktivitas/DistributionController.php index c2d7647..77e36f2 100644 --- a/app/Http/Controllers/Api/Produktivitas/DistributionController.php +++ b/app/Http/Controllers/Api/Produktivitas/DistributionController.php @@ -13,7 +13,7 @@ class DistributionController extends Controller { public function index(): JsonResponse { - if (!Gate::any(['super_admin', 'petani','fasilitator', 'offtaker'])) { + if (!Gate::any(['super_admin', 'petani','enumerator', 'offtaker'])) { abort(403); } @@ -35,7 +35,7 @@ class DistributionController extends Controller public function store(Request $request): JsonResponse { - if (!Gate::any(['super_admin', 'petani','fasilitator', 'offtaker'])) { + if (!Gate::any(['super_admin', 'petani','enumerator', 'offtaker'])) { abort(403); } @@ -60,7 +60,7 @@ class DistributionController extends Controller 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); } @@ -86,7 +86,7 @@ class DistributionController extends Controller public function batchUpsert(Request $request): JsonResponse { - if (!Gate::any(['super_admin', 'petani','fasilitator', 'offtaker'])) { + if (!Gate::any(['super_admin', 'petani','enumerator', 'offtaker'])) { abort(403); } diff --git a/app/Http/Controllers/Api/Produktivitas/LahanController.php b/app/Http/Controllers/Api/Produktivitas/LahanController.php index 6ccd277..eefeb62 100644 --- a/app/Http/Controllers/Api/Produktivitas/LahanController.php +++ b/app/Http/Controllers/Api/Produktivitas/LahanController.php @@ -21,7 +21,7 @@ class LahanController extends Controller */ public function index(Request $request): AnonymousResourceCollection { - if (!Gate::any(['petani', 'fasilitator','super_admin'])) { + if (!Gate::any(['petani', 'enumerator','super_admin'])) { abort(403); } @@ -77,7 +77,7 @@ class LahanController extends Controller */ public function store(Request $request): JsonResponse { - if (!Gate::any(['petani', 'fasilitator','super_admin'])) { + if (!Gate::any(['petani', 'enumerator','super_admin'])) { abort(403); } @@ -91,7 +91,7 @@ class LahanController extends Controller $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([ 'nama' => ['required', 'string', 'max:255'], @@ -105,11 +105,11 @@ class LahanController extends Controller 'foto.*' => ['image', 'mimes:jpeg,png,jpg', 'max:5120'], // Max 5MB ]); - $targetProfileId = $user->role === 'fasilitator' + $targetProfileId = $user->role === 'enumerator' ? $validated['profile_id'] : $profile->id; - if ($user->role === 'fasilitator') { + if ($user->role === 'enumerator') { $targetProfile = Profile::findOrFail($targetProfileId); if ($targetProfile->desa_kelurahan_id !== $profile->desa_kelurahan_id) { @@ -117,7 +117,7 @@ class LahanController extends Controller } } - $desaKelurahanId = $user->role === 'fasilitator' + $desaKelurahanId = $user->role === 'enumerator' ? $profile->desa_kelurahan_id : ($validated['desa_kelurahan_id'] ?? $profile->desa_kelurahan_id); @@ -149,14 +149,14 @@ class LahanController extends Controller */ public function show(string $id): JsonResponse { - if (!Gate::any(['petani', 'fasilitator','super_admin'])) { + if (!Gate::any(['petani', 'enumerator','super_admin'])) { abort(403); } $lahan = Lahan::with('desaKelurahan')->findOrFail($id); if ($lahan->profile_id !== Auth::user()->profile->id && Auth::user()->role === 'petani') { 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); } @@ -168,14 +168,14 @@ class LahanController extends Controller */ public function update(Request $request, string $id): JsonResponse { - if (!Gate::any(['petani', 'fasilitator','super_admin'])) { + if (!Gate::any(['petani', 'enumerator','super_admin'])) { abort(403); } $lahan = Lahan::findOrFail($id); if ($lahan->profile_id !== Auth::user()->profile->id && Auth::user()->role === 'petani') { 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); } @@ -215,14 +215,14 @@ class LahanController extends Controller */ public function destroy(string $id): JsonResponse { - if (!Gate::any(['petani', 'fasilitator','super_admin'])) { + if (!Gate::any(['petani', 'enumerator','super_admin'])) { abort(403); } $lahan = Lahan::findOrFail($id); if ($lahan->profile_id !== Auth::user()->profile->id && Auth::user()->role === 'petani') { 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); } @@ -233,7 +233,7 @@ class LahanController extends Controller public function batchUpsert(Request $request): JsonResponse { - if (!Gate::any(['petani', 'fasilitator','super_admin'])) { + if (!Gate::any(['petani', 'enumerator','super_admin'])) { abort(403); } diff --git a/app/Http/Controllers/Api/Produktivitas/OfftakerController.php b/app/Http/Controllers/Api/Produktivitas/OfftakerController.php index 9f477f4..6a1409f 100644 --- a/app/Http/Controllers/Api/Produktivitas/OfftakerController.php +++ b/app/Http/Controllers/Api/Produktivitas/OfftakerController.php @@ -12,7 +12,7 @@ class OfftakerController extends Controller { 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'); } @@ -29,7 +29,7 @@ class OfftakerController extends Controller 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'); } @@ -58,7 +58,7 @@ class OfftakerController extends Controller 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'); } $offtaker = Offtaker::findOrFail($id); @@ -67,7 +67,7 @@ class OfftakerController extends Controller 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'); } $offtaker = Offtaker::findOrFail($id); @@ -87,7 +87,7 @@ class OfftakerController extends Controller 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'); } Offtaker::findOrFail($id)->delete(); @@ -100,7 +100,7 @@ class OfftakerController extends Controller */ 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'); } diff --git a/app/Http/Controllers/Api/Produktivitas/PanenController.php b/app/Http/Controllers/Api/Produktivitas/PanenController.php index 3a0ed17..c1e256e 100644 --- a/app/Http/Controllers/Api/Produktivitas/PanenController.php +++ b/app/Http/Controllers/Api/Produktivitas/PanenController.php @@ -15,7 +15,7 @@ class PanenController extends Controller { public function index(Request $request): JsonResponse { - if (!Gate::any(['petani', 'fasilitator','super_admin'])) { + if (!Gate::any(['petani', 'enumerator','super_admin'])) { abort(403); } @@ -30,7 +30,7 @@ class PanenController extends Controller public function store(Request $request): JsonResponse { - if (!Gate::any(['petani', 'fasilitator', 'super_admin'])) { + if (!Gate::any(['petani', 'enumerator', 'super_admin'])) { abort(403); } @@ -87,7 +87,7 @@ class PanenController extends Controller public function show(string $id): JsonResponse { - if (!Gate::any(['petani', 'fasilitator','super_admin'])) { + if (!Gate::any(['petani', 'enumerator','super_admin'])) { abort(403); } $panen = Panen::findOrFail($id); @@ -96,7 +96,7 @@ class PanenController extends Controller public function update(Request $request, string $id): JsonResponse { - if (!Gate::any(['petani', 'fasilitator','super_admin'])) { + if (!Gate::any(['petani', 'enumerator','super_admin'])) { abort(403); } $panen = Panen::findOrFail($id); @@ -114,7 +114,7 @@ class PanenController extends Controller public function destroy(string $id): JsonResponse { - Gate::authorize(['petani', 'fasilitator']); + Gate::authorize(['petani', 'enumerator']); Panen::findOrFail($id)->delete(); return response()->json(null, 204); } @@ -124,7 +124,7 @@ class PanenController extends Controller */ public function batchUpsert(Request $request): JsonResponse { - if (Gate::authorize(['petani', 'fasilitator','super_admin'])) { + if (Gate::authorize(['petani', 'enumerator','super_admin'])) { abort(403); } $profileId = Auth::user()->profile->id; diff --git a/app/Http/Controllers/Api/Produktivitas/PanenController01.php b/app/Http/Controllers/Api/Produktivitas/PanenController01.php index edb49a3..7051f51 100644 --- a/app/Http/Controllers/Api/Produktivitas/PanenController01.php +++ b/app/Http/Controllers/Api/Produktivitas/PanenController01.php @@ -14,7 +14,7 @@ class PanenController01 extends Controller { public function index(Request $request): JsonResponse { - if (!Gate::any(['petani', 'fasilitator','super_admin'])) { + if (!Gate::any(['petani', 'enumerator','super_admin'])) { abort(403); } @@ -29,7 +29,7 @@ class PanenController01 extends Controller public function store(Request $request): JsonResponse { - if (!Gate::any(['petani', 'fasilitator','super_admin'])) { + if (!Gate::any(['petani', 'enumerator','super_admin'])) { abort(403); } @@ -58,7 +58,7 @@ class PanenController01 extends Controller public function show(string $id): JsonResponse { - if (!Gate::any(['petani', 'fasilitator','super_admin'])) { + if (!Gate::any(['petani', 'enumerator','super_admin'])) { abort(403); } $panen = Panen::findOrFail($id); @@ -67,7 +67,7 @@ class PanenController01 extends Controller public function update(Request $request, string $id): JsonResponse { - if (!Gate::any(['petani', 'fasilitator','super_admin'])) { + if (!Gate::any(['petani', 'enumerator','super_admin'])) { abort(403); } $panen = Panen::findOrFail($id); @@ -85,7 +85,7 @@ class PanenController01 extends Controller public function destroy(string $id): JsonResponse { - Gate::authorize(['petani', 'fasilitator']); + Gate::authorize(['petani', 'enumerator']); Panen::findOrFail($id)->delete(); return response()->json(null, 204); } @@ -95,7 +95,7 @@ class PanenController01 extends Controller */ public function batchUpsert(Request $request): JsonResponse { - if (Gate::authorize(['petani', 'fasilitator','super_admin'])) { + if (Gate::authorize(['petani', 'enumerator','super_admin'])) { abort(403); } $profileId = Auth::user()->profile->id; diff --git a/app/Http/Controllers/Api/Produktivitas/ProfileController.php b/app/Http/Controllers/Api/Produktivitas/ProfileController.php index 0a38621..4c82106 100644 --- a/app/Http/Controllers/Api/Produktivitas/ProfileController.php +++ b/app/Http/Controllers/Api/Produktivitas/ProfileController.php @@ -23,7 +23,7 @@ class ProfileController extends Controller */ public function index(Request $request) { - if(!Gate::any(['fasilitator', 'super_admin'])) { + if(!Gate::any(['enumerator', 'super_admin'])) { abort(403); } @@ -34,7 +34,7 @@ class ProfileController extends Controller $profileId = $user->role === 'petani' ? $profile->id : $requestID; $desaKelurahanId = null; - if ($profile->role === 'fasilitator' || $profile->role === 'super_admin') { + if ($profile->role === 'enumerator' || $profile->role === 'super_admin') { $desaKelurahanId = $profile->desa_kelurahan_id; } @@ -95,7 +95,7 @@ class ProfileController extends Controller */ public function store(Request $request) { - if(!Gate::any(['fasilitator','super_admin'])){ + if(!Gate::any(['enumerator','super_admin'])){ abort(403); } @@ -139,7 +139,7 @@ class ProfileController extends Controller public function show(string $id): JSONResponse { // 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); } @@ -166,7 +166,7 @@ class ProfileController extends Controller */ 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); } @@ -177,7 +177,7 @@ class ProfileController extends Controller $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'); } @@ -239,7 +239,7 @@ class ProfileController extends Controller 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); } @@ -260,7 +260,7 @@ class ProfileController extends Controller public function verify(Request $request, string $id): JSONResponse { - if (!Gate::any(['fasilitator'])) { + if (!Gate::any(['enumerator'])) { abort(403); } diff --git a/app/Http/Controllers/Api/Produktivitas/TanamanController.php b/app/Http/Controllers/Api/Produktivitas/TanamanController.php index efd00de..8daf4c6 100644 --- a/app/Http/Controllers/Api/Produktivitas/TanamanController.php +++ b/app/Http/Controllers/Api/Produktivitas/TanamanController.php @@ -22,7 +22,7 @@ class TanamanController extends Controller */ public function index(Request $request): AnonymousResourceCollection { - if (!Gate::any(['petani', 'fasilitator','super_admin'])) { + if (!Gate::any(['petani', 'enumerator','super_admin'])) { abort(403); } $user = Auth::user(); @@ -91,7 +91,7 @@ class TanamanController extends Controller */ public function store(Request $request): JsonResponse { - if (!Gate::any(['petani', 'fasilitator','super_admin'])) { + if (!Gate::any(['petani', 'enumerator','super_admin'])) { abort(403); } @@ -135,7 +135,7 @@ class TanamanController extends Controller */ public function show(string $id): JsonResponse { - if (!Gate::any(['petani', 'fasilitator','super_admin'])) { + if (!Gate::any(['petani', 'enumerator','super_admin'])) { abort(403); } @@ -160,7 +160,7 @@ class TanamanController extends Controller */ public function update(Request $request, string $id): JsonResponse { - if (!Gate::any(['petani', 'fasilitator','super_admin'])) { + if (!Gate::any(['petani', 'enumerator','super_admin'])) { abort(403); } $tanaman = Tanaman::findOrFail($id); @@ -195,7 +195,7 @@ class TanamanController extends Controller */ public function destroy(string $id): JsonResponse { - if (!Gate::any(['petani', 'fasilitator','super_admin'])) { + if (!Gate::any(['petani', 'enumerator','super_admin'])) { abort(403); } $tanaman = Tanaman::findOrFail($id); diff --git a/app/Http/Controllers/Api/TanamanController.php b/app/Http/Controllers/Api/TanamanController.php index 08cbd12..bc6d8da 100644 --- a/app/Http/Controllers/Api/TanamanController.php +++ b/app/Http/Controllers/Api/TanamanController.php @@ -22,7 +22,7 @@ class TanamanController extends Controller */ public function index(Request $request): AnonymousResourceCollection { - if (!Gate::any(['petani', 'fasilitator','super_admin'])) { + if (!Gate::any(['petani', 'enumerator','super_admin'])) { abort(403); } $user = Auth::user(); @@ -91,7 +91,7 @@ class TanamanController extends Controller */ public function store(Request $request): JsonResponse { - if (!Gate::any(['petani', 'fasilitator','super_admin'])) { + if (!Gate::any(['petani', 'enumerator','super_admin'])) { abort(403); } @@ -141,7 +141,7 @@ class TanamanController extends Controller */ public function show(string $id): JsonResponse { - if (!Gate::any(['petani', 'fasilitator','super_admin'])) { + if (!Gate::any(['petani', 'enumerator','super_admin'])) { abort(403); } @@ -166,7 +166,7 @@ class TanamanController extends Controller */ public function update(Request $request, string $id): JsonResponse { - if (!Gate::any(['petani', 'fasilitator','super_admin'])) { + if (!Gate::any(['petani', 'enumerator','super_admin'])) { abort(403); } $tanaman = Tanaman::findOrFail($id); @@ -201,7 +201,7 @@ class TanamanController extends Controller */ public function destroy(string $id): JsonResponse { - if (!Gate::any(['petani', 'fasilitator','super_admin'])) { + if (!Gate::any(['petani', 'enumerator','super_admin'])) { abort(403); } $tanaman = Tanaman::findOrFail($id); diff --git a/app/Http/Controllers/Web/Bumdes/PengurusBumdesController.php b/app/Http/Controllers/Web/Bumdes/PengurusBumdesController.php index 36ff02e..2146c49 100644 --- a/app/Http/Controllers/Web/Bumdes/PengurusBumdesController.php +++ b/app/Http/Controllers/Web/Bumdes/PengurusBumdesController.php @@ -18,7 +18,7 @@ class PengurusBumdesController extends Controller { public function index(Request $request) { - if (! Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (! Gate::any(['super_admin', 'admin', 'enumerator'])) { abort(403); } @@ -36,7 +36,7 @@ class PengurusBumdesController extends Controller }); } - if (auth()->user()->role->value === 'fasilitator') { + if (auth()->user()->role->value === 'enumerator') { $query->where('created_by', auth()->id()); } @@ -64,7 +64,7 @@ class PengurusBumdesController extends Controller public function create() { - if (! Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (! Gate::any(['super_admin', 'admin', 'enumerator'])) { abort(403); } @@ -77,7 +77,7 @@ class PengurusBumdesController extends Controller public function store(Request $request) { - if (! Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (! Gate::any(['super_admin', 'admin', 'enumerator'])) { abort(403); } @@ -100,7 +100,7 @@ class PengurusBumdesController extends Controller public function edit(string $id) { - if (! Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (! Gate::any(['super_admin', 'admin', 'enumerator'])) { abort(403); } @@ -116,7 +116,7 @@ class PengurusBumdesController extends Controller public function update(Request $request, string $id) { - if (! Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (! Gate::any(['super_admin', 'admin', 'enumerator'])) { abort(403); } @@ -133,7 +133,7 @@ class PengurusBumdesController extends Controller public function destroy(string $id) { - if (! Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (! Gate::any(['super_admin', 'admin', 'enumerator'])) { abort(403); } @@ -146,7 +146,7 @@ class PengurusBumdesController extends Controller public function kelembagaanAktor(string $id) { - if (! Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (! Gate::any(['super_admin', 'admin', 'enumerator'])) { abort(403); } @@ -166,7 +166,7 @@ class PengurusBumdesController extends Controller public function storeKelembagaanAktor(Request $request, string $id) { - if (! Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (! Gate::any(['super_admin', 'admin', 'enumerator'])) { abort(403); } @@ -191,7 +191,7 @@ class PengurusBumdesController extends Controller { $query = PengurusBumdes::query(); - if (auth()->user()->role->value === 'fasilitator') { + if (auth()->user()->role->value === 'enumerator') { $query->where('created_by', auth()->id()); } diff --git a/app/Http/Controllers/Web/Dashboard/DemografiPetaniController.php b/app/Http/Controllers/Web/Dashboard/DemografiPetaniController.php index 9226aff..e33f3c9 100644 --- a/app/Http/Controllers/Web/Dashboard/DemografiPetaniController.php +++ b/app/Http/Controllers/Web/Dashboard/DemografiPetaniController.php @@ -17,7 +17,7 @@ class DemografiPetaniController extends Controller { public function index(Request $request) { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'koordinator', 'enumerator'])) { abort(403); } @@ -28,7 +28,7 @@ class DemografiPetaniController extends Controller public function karakteristik(Request $request) { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'koordinator', 'enumerator'])) { abort(403); } diff --git a/app/Http/Controllers/Web/Dashboard/IndeksPetaniController.php b/app/Http/Controllers/Web/Dashboard/IndeksPetaniController.php index 8296256..55876b6 100644 --- a/app/Http/Controllers/Web/Dashboard/IndeksPetaniController.php +++ b/app/Http/Controllers/Web/Dashboard/IndeksPetaniController.php @@ -94,7 +94,7 @@ class IndeksPetaniController extends Controller public function index(Request $request) { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'koordinator', 'enumerator'])) { abort(403); } diff --git a/app/Http/Controllers/Web/Dashboard/InternationalMarketController.php b/app/Http/Controllers/Web/Dashboard/InternationalMarketController.php index 5d1bf3b..0efe108 100644 --- a/app/Http/Controllers/Web/Dashboard/InternationalMarketController.php +++ b/app/Http/Controllers/Web/Dashboard/InternationalMarketController.php @@ -15,7 +15,7 @@ class InternationalMarketController extends Controller { public function __invoke() { - abort_unless(Gate::allows('super_admin') || Gate::allows('admin') || Gate::allows('fasilitator'), 403); + 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'); diff --git a/app/Http/Controllers/Web/Dashboard/ProfilPetaniController.php b/app/Http/Controllers/Web/Dashboard/ProfilPetaniController.php index e9a6de9..982c9f0 100644 --- a/app/Http/Controllers/Web/Dashboard/ProfilPetaniController.php +++ b/app/Http/Controllers/Web/Dashboard/ProfilPetaniController.php @@ -17,7 +17,7 @@ class ProfilPetaniController extends Controller { public function index() { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'koordinator', 'enumerator'])) { abort(403); } $title = 'Dashboard Profil Petani'; diff --git a/app/Http/Controllers/Web/Dashboard/SummaryController.php b/app/Http/Controllers/Web/Dashboard/SummaryController.php index 122d280..0fccdb8 100644 --- a/app/Http/Controllers/Web/Dashboard/SummaryController.php +++ b/app/Http/Controllers/Web/Dashboard/SummaryController.php @@ -37,7 +37,7 @@ class SummaryController extends Controller public function produktivitas(Request $request) { - if (!Gate::allows('super_admin') && !Gate::allows('admin') && !Gate::allows('fasilitator')) { + if (!Gate::allows('super_admin') && !Gate::allows('admin') && !Gate::allows('koordinator') && !Gate::allows('enumerator')) { abort(403); } @@ -59,7 +59,7 @@ class SummaryController extends Controller public function mapInformation(Request $request) { - if (!Gate::allows('super_admin') && !Gate::allows('admin') && !Gate::allows('fasilitator')) { + if (!Gate::allows('super_admin') && !Gate::allows('admin') && !Gate::allows('koordinator') && !Gate::allows('enumerator')) { abort(403); } @@ -101,7 +101,7 @@ class SummaryController extends Controller public function pasar(Request $request) { - if (!Gate::allows('super_admin') && !Gate::allows('admin') && !Gate::allows('fasilitator')) { + if (!Gate::allows('super_admin') && !Gate::allows('admin') && !Gate::allows('koordinator') && !Gate::allows('enumerator')) { abort(403); } diff --git a/app/Http/Controllers/Web/Dashboard/SurveyPetaniController.php b/app/Http/Controllers/Web/Dashboard/SurveyPetaniController.php index 21d92ea..1f35e33 100644 --- a/app/Http/Controllers/Web/Dashboard/SurveyPetaniController.php +++ b/app/Http/Controllers/Web/Dashboard/SurveyPetaniController.php @@ -31,7 +31,7 @@ class SurveyPetaniController extends Controller // Progres per enumerator $enumerators = User::query() - ->where('role', 'fasilitator') + ->where('role', 'enumerator') ->withCount([ 'petaniDibuat as done', ]) diff --git a/app/Http/Controllers/Web/Ekosistem/PetaniCPMController.php b/app/Http/Controllers/Web/Ekosistem/PetaniCPMController.php index 6509798..f0c997d 100644 --- a/app/Http/Controllers/Web/Ekosistem/PetaniCPMController.php +++ b/app/Http/Controllers/Web/Ekosistem/PetaniCPMController.php @@ -24,7 +24,7 @@ class PetaniCPMController extends Controller { public function index(Request $request): View { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'enumerator'])) { abort(403); } @@ -60,7 +60,7 @@ class PetaniCPMController extends Controller public function create(Request $request): View { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'enumerator'])) { abort(403); } @@ -94,7 +94,7 @@ class PetaniCPMController extends Controller */ public function store(Request $request): RedirectResponse { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'enumerator'])) { abort(403); } @@ -147,7 +147,7 @@ class PetaniCPMController extends Controller */ public function show(string $id): View { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'enumerator'])) { abort(403); } @@ -161,7 +161,7 @@ class PetaniCPMController extends Controller */ public function edit(Request $request, string $id): View { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'enumerator'])) { abort(403); } @@ -198,7 +198,7 @@ class PetaniCPMController extends Controller */ public function update(Request $request, string $id) { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'enumerator'])) { abort(403); } @@ -251,7 +251,7 @@ class PetaniCPMController extends Controller */ public function destroy(string $id) { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'enumerator'])) { abort(403); } diff --git a/app/Http/Controllers/Web/Ekosistem/ValueBasisDataController.php b/app/Http/Controllers/Web/Ekosistem/ValueBasisDataController.php index fef69e5..3230455 100644 --- a/app/Http/Controllers/Web/Ekosistem/ValueBasisDataController.php +++ b/app/Http/Controllers/Web/Ekosistem/ValueBasisDataController.php @@ -34,7 +34,7 @@ class ValueBasisDataController extends Controller public function index(Request $request, string $tabelId): View { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'enumerator'])) { abort(403); } diff --git a/app/Http/Controllers/Web/Konfigurasi/KoordinatorKomoditasController.php b/app/Http/Controllers/Web/Konfigurasi/KoordinatorKomoditasController.php new file mode 100644 index 0000000..adf53f9 --- /dev/null +++ b/app/Http/Controllers/Web/Konfigurasi/KoordinatorKomoditasController.php @@ -0,0 +1,70 @@ +isKoordinator()) { + return response()->json([ + 'success' => false, + 'message' => 'Komoditas hanya bisa diatur untuk user dengan role Koordinator.', + ], 422); + } + + $validated = $request->validate([ + 'komoditas_ids' => ['required', 'array', 'min:1'], + 'komoditas_ids.*' => ['integer', 'exists:master_komoditas,id'], + ], [ + 'komoditas_ids.required' => 'Pilih minimal satu komoditas.', + 'komoditas_ids.min' => 'Pilih minimal satu komoditas.', + 'komoditas_ids.*.exists' => 'Komoditas yang dipilih tidak valid.', + ]); + + self::simpanPenugasan($user, $validated['komoditas_ids']); + + return response()->json([ + 'success' => true, + 'message' => 'Komoditas koordinator berhasil diperbarui.', + 'data' => $user->komoditasKoordinatorIds(), + ]); + } + + /** + * Menyamakan penugasan koordinator dengan daftar komoditas yang dipilih. + * Penugasan yang dilepas di-soft delete, penugasan baru dibuat. + */ + public static function simpanPenugasan(User $user, array $komoditasIds): void + { + $komoditasIds = array_values(array_unique(array_map('intval', $komoditasIds))); + + DB::transaction(function () use ($user, $komoditasIds) { + $user->koordinatorKomoditas() + ->whereNotIn('komoditas_id', $komoditasIds) + ->get() + ->each->delete(); + + $sudahAda = $user->komoditasKoordinatorIds(); + + foreach (array_diff($komoditasIds, $sudahAda) as $komoditasId) { + $user->koordinatorKomoditas()->create([ + 'komoditas_id' => $komoditasId, + 'created_by' => auth()->id(), + 'updated_by' => auth()->id(), + ]); + } + }); + } +} diff --git a/app/Http/Controllers/Web/Konfigurasi/UserController.php b/app/Http/Controllers/Web/Konfigurasi/UserController.php index 4931772..3bf49b4 100644 --- a/app/Http/Controllers/Web/Konfigurasi/UserController.php +++ b/app/Http/Controllers/Web/Konfigurasi/UserController.php @@ -8,16 +8,29 @@ use App\Models\User; use App\Models\Profile; use App\Enums\Role; use App\Models\Wilayah\Provinsi; +use App\Models\Pertanian\Komoditas; use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Gate; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Validator; use Illuminate\Support\Str; class UserController extends Controller { + // Manajemen pengguna hanya untuk super admin & admin (sama dengan menu "Daftar Pengguna"). + // Berlaku untuk semua method di controller ini. + public function __construct() + { + $this->middleware(function (Request $request, \Closure $next) { + abort_unless($request->user() && Gate::forUser($request->user())->any(['super_admin', 'admin']), 403); + + return $next($request); + }); + } + public function index(Request $request) { - $query = User::orderBy('created_at', 'desc'); + $query = User::with('koordinatorKomoditas.komoditas:id,nama')->orderBy('created_at', 'desc'); $tab = $request->get('tab', 'active'); if ($tab === 'pending') { @@ -45,8 +58,15 @@ class UserController extends Controller $pendingUsers = User::whereNull('email_verified_at')->count(); $roles = Role::cases(); + $komoditasOptions = $this->komoditasOptions(); - return view('konfigurasi.user.index', compact('users', 'totalUsers', 'activeUsers', 'pendingUsers', 'roles', 'tab')); + return view('konfigurasi.user.index', compact('users', 'totalUsers', 'activeUsers', 'pendingUsers', 'roles', 'tab', 'komoditasOptions')); + } + + // Komoditas utama yang bisa ditugaskan ke koordinator + private function komoditasOptions() + { + return Komoditas::select('id', 'nama')->where('is_utama', true)->orderBy('nama')->get(); } public function verify(Request $request, $id) @@ -92,8 +112,9 @@ class UserController extends Controller { $roles = Role::cases(); $provinsi = Provinsi::orderBy('nama', 'asc')->get(); + $komoditasOptions = $this->komoditasOptions(); - return view('konfigurasi.user.add', compact('roles', 'provinsi')); + return view('konfigurasi.user.add', compact('roles', 'provinsi', 'komoditasOptions')); } public function store(Request $request) @@ -104,6 +125,8 @@ class UserController extends Controller 'email' => ['required', 'string', 'email', 'max:255', 'unique:users,email'], 'password' => ['required', 'string', 'min:8', 'confirmed'], 'role' => ['required', 'string'], + 'komoditas_ids' => ['nullable', 'array', 'required_if:role,' . Role::KOORDINATOR->value], + 'komoditas_ids.*' => ['integer', 'exists:master_komoditas,id'], 'telepon' => ['required', 'max:13'], 'desa_kelurahan_id' => ['nullable', 'exists:desa_kelurahan,id'], 'ktp' => ['nullable', 'min:16', 'max:16'], @@ -116,6 +139,7 @@ class UserController extends Controller 'password.confirmed' => 'Konfirmasi password tidak cocok!', 'password.min' => 'Password minimal 8 karakter!', 'email.unique' => 'Email sudah terdaftar.', + 'komoditas_ids.required_if' => 'Pilih minimal satu komoditas untuk koordinator.', 'required' => 'Kolom :attribute wajib diisi.' ]); @@ -163,6 +187,10 @@ class UserController extends Controller 'created_by' => auth()->id(), 'updated_by' => auth()->id(), ]); + + if ($user->isKoordinator()) { + KoordinatorKomoditasController::simpanPenugasan($user, $request->input('komoditas_ids', [])); + } }); if ($request->expectsJson()) { diff --git a/app/Http/Controllers/Web/Produktivitas/AsetKelompokTaniController.php b/app/Http/Controllers/Web/Produktivitas/AsetKelompokTaniController.php index b69b0d4..bfbc252 100644 --- a/app/Http/Controllers/Web/Produktivitas/AsetKelompokTaniController.php +++ b/app/Http/Controllers/Web/Produktivitas/AsetKelompokTaniController.php @@ -27,7 +27,7 @@ class AsetKelompokTaniController extends Controller private function authorizeAccess(): void { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'enumerator'])) { abort(403); } } diff --git a/app/Http/Controllers/Web/Produktivitas/BantuanKelompokTaniController.php b/app/Http/Controllers/Web/Produktivitas/BantuanKelompokTaniController.php index 035ecbd..983e68e 100644 --- a/app/Http/Controllers/Web/Produktivitas/BantuanKelompokTaniController.php +++ b/app/Http/Controllers/Web/Produktivitas/BantuanKelompokTaniController.php @@ -16,7 +16,7 @@ class BantuanKelompokTaniController extends Controller { private function authorizeAccess(): void { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'enumerator'])) { abort(403); } } diff --git a/app/Http/Controllers/Web/Produktivitas/PendidikanPelatihanController.php b/app/Http/Controllers/Web/Produktivitas/PendidikanPelatihanController.php index fb8e686..de55180 100644 --- a/app/Http/Controllers/Web/Produktivitas/PendidikanPelatihanController.php +++ b/app/Http/Controllers/Web/Produktivitas/PendidikanPelatihanController.php @@ -17,7 +17,7 @@ class PendidikanPelatihanController extends Controller { private function authorizeAccess(): void { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'koordinator', 'enumerator'])) { abort(403); } } diff --git a/app/Http/Controllers/Web/Produktivitas/PengurusKelompokTaniController.php b/app/Http/Controllers/Web/Produktivitas/PengurusKelompokTaniController.php index e89ad09..b5b12cb 100644 --- a/app/Http/Controllers/Web/Produktivitas/PengurusKelompokTaniController.php +++ b/app/Http/Controllers/Web/Produktivitas/PengurusKelompokTaniController.php @@ -14,7 +14,7 @@ class PengurusKelompokTaniController extends Controller { private function authorizeAccess(): void { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'enumerator'])) { abort(403); } } diff --git a/app/Http/Controllers/Web/Produktivitas/PetaniController.php b/app/Http/Controllers/Web/Produktivitas/PetaniController.php index 9d707a4..cf5bac4 100644 --- a/app/Http/Controllers/Web/Produktivitas/PetaniController.php +++ b/app/Http/Controllers/Web/Produktivitas/PetaniController.php @@ -32,13 +32,13 @@ class PetaniController extends Controller */ public function index(Request $request) { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'koordinator', 'enumerator'])) { abort(403); } $provinsi = Provinsi::select('id', 'kode', 'nama')->orderBy('kode', 'asc')->get(); $userRole = auth()->user()->role->value; - $showFasilitatorColumn = in_array($userRole, ['super_admin', 'admin'], true); + $showFasilitatorColumn = in_array($userRole, ['super_admin', 'admin', 'koordinator'], true); $defaultKabupaten = KabupatenKota::select('id', 'nama') ->whereRaw('lower(nama) like ?', ['%ngada%']) ->orderBy('nama') @@ -102,10 +102,19 @@ class PetaniController extends Controller $query->where('desa_kelurahan_id', $request->desa_kelurahan_id); } - if ($userRole === 'fasilitator') { + if ($userRole === 'enumerator') { $query->where('created_by', auth()->user()->id); } + // Koordinator hanya melihat petani dengan komoditas yang ditugaskan + $komoditasKoordinatorIds = $userRole === 'koordinator' + ? auth()->user()->komoditasKoordinatorIds() + : null; + + if ($komoditasKoordinatorIds !== null) { + $query->whereIn('komoditas_id', $komoditasKoordinatorIds); + } + $totalPetani = (clone $query)->count(); $totalPetaniLakiLaki = (clone $query)->where('jenis_kelamin', 1)->count(); $totalPetaniPerempuan = (clone $query)->where('jenis_kelamin', 0)->count(); @@ -165,10 +174,15 @@ class PetaniController extends Controller ->orWhereRaw('lower(ktp) like ?', ["%{$search}%"]); }); }) - ->when($userRole === 'fasilitator', function ($query) { + ->when($userRole === 'enumerator', function ($query) { $query->whereHas('petani', function ($petani) { $petani->where('created_by', auth()->id()); }); + }) + ->when($komoditasKoordinatorIds !== null, function ($query) use ($komoditasKoordinatorIds) { + $query->whereHas('petani', function ($petani) use ($komoditasKoordinatorIds) { + $petani->whereIn('komoditas_id', $komoditasKoordinatorIds); + }); }); $totalLahan = (clone $lahanSummaryQuery)->count(); @@ -216,7 +230,7 @@ class PetaniController extends Controller public function create() { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'koordinator', 'enumerator'])) { abort(403); } @@ -288,7 +302,7 @@ class PetaniController extends Controller public function store(Request $request) { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'koordinator', 'enumerator'])) { abort(403); } @@ -320,7 +334,7 @@ class PetaniController extends Controller public function show(string $id) { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'koordinator', 'enumerator'])) { abort(403); } @@ -332,7 +346,7 @@ class PetaniController extends Controller public function edit(string $id, Request $request) { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'koordinator', 'enumerator'])) { abort(403); } @@ -353,7 +367,7 @@ class PetaniController extends Controller public function update(Request $request, string $id) { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'koordinator', 'enumerator'])) { abort(403); } @@ -395,14 +409,14 @@ class PetaniController extends Controller public function destroy(string $id) { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'koordinator', 'enumerator'])) { return response()->json(['message' => 'Anda tidak memiliki akses.'], 403); } try { $petani = Petani::findOrFail($id); - if (auth()->user()->role->value === 'fasilitator' && $petani->created_by !== auth()->id()) { + if (auth()->user()->role->value === 'enumerator' && $petani->created_by !== auth()->id()) { return response()->json(['message' => 'Anda tidak memiliki akses.'], 403); } @@ -429,7 +443,7 @@ class PetaniController extends Controller public function showImage(string $id, string $field) { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'koordinator', 'enumerator'])) { abort(403); } diff --git a/app/Http/Controllers/Web/Produktivitas/PetaniLahanController.php b/app/Http/Controllers/Web/Produktivitas/PetaniLahanController.php index 1a20417..294689e 100644 --- a/app/Http/Controllers/Web/Produktivitas/PetaniLahanController.php +++ b/app/Http/Controllers/Web/Produktivitas/PetaniLahanController.php @@ -32,7 +32,7 @@ class PetaniLahanController extends Controller { public function index($petaniId, Request $request) { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'koordinator', 'enumerator'])) { abort(403); } @@ -52,7 +52,7 @@ class PetaniLahanController extends Controller public function create($petaniId) { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'koordinator', 'enumerator'])) { abort(403); } @@ -194,7 +194,7 @@ class PetaniLahanController extends Controller public function store($petaniId, Request $request) { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'koordinator', 'enumerator'])) { abort(403); } @@ -292,7 +292,7 @@ class PetaniLahanController extends Controller public function show(string $petaniId, string $id) { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'koordinator', 'enumerator'])) { abort(403); } @@ -310,7 +310,7 @@ class PetaniLahanController extends Controller public function edit(string $petaniId, string $id) { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'koordinator', 'enumerator'])) { abort(403); } @@ -335,7 +335,7 @@ class PetaniLahanController extends Controller public function update(Request $request, string $petaniId, string $id) { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'koordinator', 'enumerator'])) { abort(403); } @@ -462,7 +462,7 @@ class PetaniLahanController extends Controller public function destroy(string $petaniId, string $id) { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'koordinator', 'enumerator'])) { abort(403); } @@ -481,7 +481,7 @@ class PetaniLahanController extends Controller public function summary(string $petaniId) { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'koordinator', 'enumerator'])) { abort(403); } diff --git a/app/Http/Controllers/Web/Produktivitas/PetaniLahanTanamanController.php b/app/Http/Controllers/Web/Produktivitas/PetaniLahanTanamanController.php index 62896a4..eaa5f2e 100644 --- a/app/Http/Controllers/Web/Produktivitas/PetaniLahanTanamanController.php +++ b/app/Http/Controllers/Web/Produktivitas/PetaniLahanTanamanController.php @@ -42,7 +42,7 @@ class PetaniLahanTanamanController extends Controller */ public function show(string $petaniId, string $lahanId, string $id) { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'koordinator', 'enumerator'])) { abort(403); } @@ -63,7 +63,7 @@ class PetaniLahanTanamanController extends Controller public function update(Request $request, string $petaniId, string $lahanId, string $id) { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'koordinator', 'enumerator'])) { abort(403); } @@ -103,7 +103,7 @@ class PetaniLahanTanamanController extends Controller */ public function destroy(string $petaniId, string $lahanId, string $id) { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'koordinator', 'enumerator'])) { abort(403); } diff --git a/app/Http/Controllers/Web/Produktivitas/PraktikBudidayaController.php b/app/Http/Controllers/Web/Produktivitas/PraktikBudidayaController.php index acfbe1c..e90ec32 100644 --- a/app/Http/Controllers/Web/Produktivitas/PraktikBudidayaController.php +++ b/app/Http/Controllers/Web/Produktivitas/PraktikBudidayaController.php @@ -42,7 +42,7 @@ class PraktikBudidayaController extends Controller { public function index($petaniId, Request $request) { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'koordinator', 'enumerator'])) { abort(403); } @@ -139,7 +139,7 @@ class PraktikBudidayaController extends Controller public function store($petaniId, Request $request) { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'koordinator', 'enumerator'])) { abort(403); } @@ -247,7 +247,7 @@ class PraktikBudidayaController extends Controller public function edit($petaniId, $praktikBudidayaId) { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'koordinator', 'enumerator'])) { abort(403); } @@ -340,7 +340,7 @@ class PraktikBudidayaController extends Controller public function update($petaniId, $praktikBudidayaId, Request $request) { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'koordinator', 'enumerator'])) { abort(403); } diff --git a/app/Http/Controllers/Web/Produktivitas/SkorPetaniController.php b/app/Http/Controllers/Web/Produktivitas/SkorPetaniController.php index 195a509..802bf7b 100644 --- a/app/Http/Controllers/Web/Produktivitas/SkorPetaniController.php +++ b/app/Http/Controllers/Web/Produktivitas/SkorPetaniController.php @@ -16,7 +16,7 @@ class SkorPetaniController extends Controller */ public function index(string $petaniId) { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'koordinator', 'enumerator'])) { abort(403); } diff --git a/app/Http/Controllers/Web/Produktivitas/SurveyKelompokTaniController.php b/app/Http/Controllers/Web/Produktivitas/SurveyKelompokTaniController.php index 16ba477..bd939e6 100644 --- a/app/Http/Controllers/Web/Produktivitas/SurveyKelompokTaniController.php +++ b/app/Http/Controllers/Web/Produktivitas/SurveyKelompokTaniController.php @@ -19,7 +19,7 @@ class SurveyKelompokTaniController extends Controller { private function authorizeAccess(): void { - if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + if (!Gate::any(['super_admin', 'admin', 'enumerator'])) { abort(403); } } diff --git a/app/Http/Middleware/BatasiAksesPetani.php b/app/Http/Middleware/BatasiAksesPetani.php new file mode 100644 index 0000000..f48d6f8 --- /dev/null +++ b/app/Http/Middleware/BatasiAksesPetani.php @@ -0,0 +1,45 @@ +user(); + + abort_unless($user && in_array($user->role, self::ROLE_DIIZINKAN, true), 403); + + $petani = $request->route('petani'); + + if ($petani !== null && $user->isKoordinator()) { + $petani = $petani instanceof Petani ? $petani : Petani::find($petani); + + abort_unless( + $petani && $user->bolehAksesPetani($petani), + 403, + 'Petani ini bukan bagian dari komoditas yang Anda koordinasikan.' + ); + } + + return $next($request); + } +} diff --git a/app/Models/Concerns/HasDeletedBy.php b/app/Models/Concerns/HasDeletedBy.php new file mode 100644 index 0000000..6a6a069 --- /dev/null +++ b/app/Models/Concerns/HasDeletedBy.php @@ -0,0 +1,17 @@ +isForceDeleting()) { + $model->deleted_by = auth()->id(); + $model->saveQuietly(); + } + }); + } +} diff --git a/app/Models/Konfigurasi/KoordinatorKomoditas.php b/app/Models/Konfigurasi/KoordinatorKomoditas.php new file mode 100644 index 0000000..11dbbde --- /dev/null +++ b/app/Models/Konfigurasi/KoordinatorKomoditas.php @@ -0,0 +1,34 @@ +belongsTo(User::class, 'user_id'); + } + + public function komoditas(): BelongsTo + { + return $this->belongsTo(Komoditas::class, 'komoditas_id'); + } +} diff --git a/app/Models/User.php b/app/Models/User.php index d34f3ed..36d45b2 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -12,6 +12,7 @@ use Illuminate\Database\Eloquent\Relations\HasOne; use Illuminate\Database\Eloquent\Relations\HasMany; use Laravel\Sanctum\HasApiTokens; +use App\Models\Konfigurasi\KoordinatorKomoditas; use App\Models\Pertanian\Petani; class User extends Authenticatable @@ -52,4 +53,42 @@ class User extends Authenticatable { return $this->hasMany(Petani::class, 'created_by'); } + + protected static function booted(): void + { + static::deleting(function (User $user) { + if ($user->isForceDeleting()) { + return; + } + + $user->koordinatorKomoditas()->get()->each->delete(); + }); + } + + public function koordinatorKomoditas(): HasMany + { + return $this->hasMany(KoordinatorKomoditas::class, 'user_id'); + } + + public function isKoordinator(): bool + { + return $this->role === Role::KOORDINATOR; + } + + // Daftar id komoditas yang ditugaskan ke koordinator ini. + public function komoditasKoordinatorIds(): array + { + return $this->koordinatorKomoditas()->pluck('komoditas_id')->all(); + } + + // Koordinator hanya boleh mengakses petani dengan komoditas utama yang ditugaskan. + // Role lain tidak dibatasi di sini (aturannya tetap di masing-masing controller). + public function bolehAksesPetani(Petani $petani): bool + { + if (! $this->isKoordinator()) { + return true; + } + + return in_array($petani->komoditas_id, $this->komoditasKoordinatorIds()); + } } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index d8209f0..86fd286 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -54,6 +54,14 @@ class AppServiceProvider extends ServiceProvider return $user->role === Role::ADMIN; }); + Gate::define('koordinator', function (User $user) { + return $user->role === Role::KOORDINATOR; + }); + + Gate::define('enumerator', function (User $user) { + return $user->role === Role::ENUMERATOR; + }); + Gate::define('fasilitator', function (User $user) { return $user->role === Role::FASILITATOR; }); diff --git a/bootstrap/app.php b/bootstrap/app.php index 178e8ce..df1f9c4 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -15,6 +15,10 @@ return Application::configure(basePath: dirname(__DIR__)) ) ->withMiddleware(function (Middleware $middleware): void { $middleware->append(\App\Http\Middleware\ResolveProgram::class); + + $middleware->alias([ + 'akses.petani' => \App\Http\Middleware\BatasiAksesPetani::class, + ]); }) ->withExceptions(function (Exceptions $exceptions): void { // diff --git a/database/migrations/2026_11_04_100000_create_koordinator_komoditas_table.php b/database/migrations/2026_11_04_100000_create_koordinator_komoditas_table.php new file mode 100644 index 0000000..5f0da5f --- /dev/null +++ b/database/migrations/2026_11_04_100000_create_koordinator_komoditas_table.php @@ -0,0 +1,34 @@ +id(); + $table->foreignId('user_id')->constrained('users'); + $table->foreignId('komoditas_id')->constrained('master_komoditas'); + + $table->foreignId('created_by')->nullable()->constrained('users'); + $table->foreignId('updated_by')->nullable()->constrained('users'); + $table->timestamps(); + $table->softDeletes(); + $table->foreignId('deleted_by')->nullable()->constrained('users'); + + // Bukan unique index (sesuai AISTANDAR 5.2), cukup index untuk pencarian + $table->index(['user_id', 'komoditas_id']); + }); + } + + public function down(): void + { + Schema::dropIfExists('koordinator_komoditas'); + } +}; diff --git a/resources/views/konfigurasi/user/add.blade.php b/resources/views/konfigurasi/user/add.blade.php index 52ddf16..83af294 100644 --- a/resources/views/konfigurasi/user/add.blade.php +++ b/resources/views/konfigurasi/user/add.blade.php @@ -47,7 +47,7 @@ @foreach($roles as $roleEnum) @endforeach @@ -55,6 +55,19 @@ + {{-- Hanya untuk role Koordinator --}} +