Update Role fasilitator, kordinator
This commit is contained in:
+4
-2
@@ -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.
|
||||
|
||||
---
|
||||
|
||||
+16
-1
@@ -6,7 +6,22 @@ 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',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ class SurveyPetaniController extends Controller
|
||||
// Progres per enumerator
|
||||
|
||||
$enumerators = User::query()
|
||||
->where('role', 'fasilitator')
|
||||
->where('role', 'enumerator')
|
||||
->withCount([
|
||||
'petaniDibuat as done',
|
||||
])
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Web\Konfigurasi;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
/**
|
||||
* Mengatur komoditas yang ditangani seorang koordinator.
|
||||
*/
|
||||
class KoordinatorKomoditasController extends Controller
|
||||
{
|
||||
public function update(Request $request, User $user)
|
||||
{
|
||||
abort_unless(Gate::any(['super_admin', 'admin']), 403);
|
||||
|
||||
if (! $user->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(),
|
||||
]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use App\Enums\Role;
|
||||
use App\Models\Pertanian\Petani;
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* Dipasang di semua route modul data petani (petani/{petani}/...).
|
||||
* - Hanya role pengelola data petani yang boleh masuk.
|
||||
* - Koordinator hanya boleh mengakses petani dengan komoditas utama yang ditugaskan.
|
||||
*/
|
||||
class BatasiAksesPetani
|
||||
{
|
||||
private const ROLE_DIIZINKAN = [
|
||||
Role::SUPER_ADMIN,
|
||||
Role::ADMIN,
|
||||
Role::KOORDINATOR,
|
||||
Role::ENUMERATOR,
|
||||
];
|
||||
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
$user = $request->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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Concerns;
|
||||
|
||||
// Mengisi kolom deleted_by otomatis saat data di-soft delete (AISTANDAR 5.3).
|
||||
trait HasDeletedBy
|
||||
{
|
||||
protected static function bootHasDeletedBy(): void
|
||||
{
|
||||
static::deleting(function ($model) {
|
||||
if (! $model->isForceDeleting()) {
|
||||
$model->deleted_by = auth()->id();
|
||||
$model->saveQuietly();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Konfigurasi;
|
||||
|
||||
use App\Models\Concerns\HasDeletedBy;
|
||||
use App\Models\Pertanian\Komoditas;
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class KoordinatorKomoditas extends Model
|
||||
{
|
||||
use SoftDeletes, HasDeletedBy;
|
||||
|
||||
protected $table = 'koordinator_komoditas';
|
||||
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'komoditas_id',
|
||||
'created_by',
|
||||
'updated_by',
|
||||
];
|
||||
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class, 'user_id');
|
||||
}
|
||||
|
||||
public function komoditas(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Komoditas::class, 'komoditas_id');
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
@@ -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 {
|
||||
//
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Penugasan user koordinator ke komoditas yang dia tangani.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('koordinator_komoditas', function (Blueprint $table) {
|
||||
$table->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');
|
||||
}
|
||||
};
|
||||
@@ -47,7 +47,7 @@
|
||||
<option value="">Pilih Role</option>
|
||||
@foreach($roles as $roleEnum)
|
||||
<option value="{{ $roleEnum->value }}" {{ old('role', \App\Enums\Role::PETANI->value) === $roleEnum->value ? 'selected' : '' }}>
|
||||
{{ ucfirst(str_replace('_', ' ', $roleEnum->value)) }}
|
||||
{{ $roleEnum->label() }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@@ -55,6 +55,19 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Hanya untuk role Koordinator --}}
|
||||
<div class="row {{ old('role') === \App\Enums\Role::KOORDINATOR->value ? '' : 'd-none' }}" id="koordinator-komoditas-wrapper">
|
||||
<div class="col-md-12 mb-4 form-control-validation">
|
||||
<label class="form-label" for="add-koordinator-komoditas">Komoditas yang Dikoordinasikan<span class="text-danger">*</span></label>
|
||||
<select id="add-koordinator-komoditas" name="komoditas_ids[]" class="select2 form-select @error('komoditas_ids') is-invalid @enderror" multiple data-placeholder="Pilih Komoditas" style="width: 100%">
|
||||
@foreach($komoditasOptions as $komoditas)
|
||||
<option value="{{ $komoditas->id }}" @selected(in_array($komoditas->id, old('komoditas_ids', [])))>{{ $komoditas->nama }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@error('komoditas_ids')<div class="invalid-feedback d-block">{{ $message }}</div>@enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-4 form-control-validation">
|
||||
<label class="form-label" for="add-profile-telepon">No. Telepon<span class="text-danger">*</span></label>
|
||||
@@ -183,6 +196,12 @@
|
||||
$(document).ready(function () {
|
||||
$('.select2').select2();
|
||||
|
||||
// Pilihan komoditas hanya tampil untuk role Koordinator
|
||||
$('#add-profile-role').on('change', function () {
|
||||
const isKoordinator = $(this).val() === @json(\App\Enums\Role::KOORDINATOR->value);
|
||||
$('#koordinator-komoditas-wrapper').toggleClass('d-none', !isKoordinator);
|
||||
});
|
||||
|
||||
$(document).on('click', '.password-toggle', function () {
|
||||
const input = this.closest('.input-group').querySelector('input');
|
||||
const icon = this.querySelector('i');
|
||||
|
||||
@@ -111,7 +111,7 @@
|
||||
<option value="">-- All Roles --</option>
|
||||
@foreach($roles as $roleEnum)
|
||||
<option value="{{ $roleEnum->value }}" {{ request('role') == $roleEnum->value ? 'selected' : '' }}>
|
||||
{{ ucfirst(str_replace('_', ' ', $roleEnum->value)) }}
|
||||
{{ $roleEnum->label() }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@@ -164,14 +164,24 @@
|
||||
$roleColors = [
|
||||
'super_admin' => 'bg-label-danger',
|
||||
'admin' => 'bg-label-warning',
|
||||
'fasilitator' => 'bg-label-info',
|
||||
'koordinator' => 'bg-label-primary',
|
||||
'enumerator' => 'bg-label-info',
|
||||
'fasilitator' => 'bg-label-dark',
|
||||
'petani' => 'bg-label-success',
|
||||
];
|
||||
$color = $roleColors[$roleKey] ?? 'bg-label-secondary';
|
||||
$roleLabel = ($user->role instanceof \App\Enums\Role)
|
||||
? $user->role->label()
|
||||
: ucfirst(str_replace('_', ' ', (string) $roleKey));
|
||||
@endphp
|
||||
<span class="badge {{ $color }}">
|
||||
{{ ucfirst(str_replace('_', ' ', $roleKey)) }}
|
||||
{{ $roleLabel }}
|
||||
</span>
|
||||
@if($roleKey === 'koordinator')
|
||||
<small class="d-block text-muted mt-1">
|
||||
{{ $user->koordinatorKomoditas->pluck('komoditas.nama')->filter()->join(', ') ?: 'Belum ada komoditas' }}
|
||||
</small>
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
@if($user->email_verified_at)
|
||||
@@ -192,6 +202,16 @@
|
||||
<i class="ti tabler-trash ti-md"></i>
|
||||
</a>
|
||||
|
||||
@if($roleKey === 'koordinator')
|
||||
<a href="javascript:;" class="btn btn-icon btn-text-secondary rounded-pill btn-komoditas-koordinator"
|
||||
title="Atur Komoditas"
|
||||
data-url="{{ route('users.komoditas.update', $user->id) }}"
|
||||
data-name="{{ $user->name }}"
|
||||
data-selected='@json($user->koordinatorKomoditas->pluck('komoditas_id')->values())'>
|
||||
<i class="ti tabler-plant ti-md"></i>
|
||||
</a>
|
||||
@endif
|
||||
|
||||
@if(!$user->email_verified_at)
|
||||
<button type="button"
|
||||
class="btn btn-primary px-9 d-flex align-items-center btn-verify shadow-sm waves-effect waves-light"
|
||||
@@ -241,7 +261,9 @@
|
||||
<option value="" selected disabled>-- Pilih Role Akses --</option>
|
||||
<option value="super_admin">🟣 Super Admin</option>
|
||||
<option value="admin">🟣 Admin</option>
|
||||
<option value="fasilitator">🟣 Fasilitator</option>
|
||||
<option value="koordinator">🟣 Koordinator</option>
|
||||
<option value="enumerator">🟣 Enumerator</option>
|
||||
<option value="fasilitator">🟣 Fasilitator (Pemateri Workshop)</option>
|
||||
<option value="offtaker">🟣 Offtaker</option>
|
||||
</select>
|
||||
</div>
|
||||
@@ -395,4 +417,67 @@
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
// Atur komoditas yang ditangani koordinator
|
||||
$(function () {
|
||||
const komoditasOptions = @json($komoditasOptions);
|
||||
|
||||
$(document).on('click', '.btn-komoditas-koordinator', function () {
|
||||
const url = $(this).data('url');
|
||||
const nama = $(this).data('name');
|
||||
const selected = ($(this).data('selected') || []).map(Number);
|
||||
|
||||
const checkboxes = komoditasOptions.map(function (k) {
|
||||
const checked = selected.includes(k.id) ? 'checked' : '';
|
||||
return `<div class="form-check text-start mb-2">
|
||||
<input class="form-check-input komoditas-koordinator-check" type="checkbox" value="${k.id}" id="kk-${k.id}" ${checked}>
|
||||
<label class="form-check-label" for="kk-${k.id}">${$('<div>').text(k.nama).html()}</label>
|
||||
</div>`;
|
||||
}).join('');
|
||||
|
||||
Swal.fire({
|
||||
title: 'Komoditas Koordinator',
|
||||
html: `<p class="text-muted mb-4">Pilih komoditas yang dikoordinasikan oleh <strong>${$('<div>').text(nama).html()}</strong>.</p>
|
||||
<div class="px-4">${checkboxes || '<p class="text-muted">Belum ada komoditas utama.</p>'}</div>`,
|
||||
showCancelButton: true,
|
||||
confirmButtonText: '<i class="ti tabler-check me-1"></i> Simpan',
|
||||
cancelButtonText: 'Batal',
|
||||
customClass: {
|
||||
popup: 'rounded-4',
|
||||
confirmButton: 'btn btn-primary me-3',
|
||||
cancelButton: 'btn btn-label-secondary'
|
||||
},
|
||||
buttonsStyling: false,
|
||||
preConfirm: function () {
|
||||
const ids = Array.from(Swal.getPopup().querySelectorAll('.komoditas-koordinator-check:checked')).map(el => el.value);
|
||||
if (ids.length === 0) {
|
||||
Swal.showValidationMessage('Pilih minimal satu komoditas.');
|
||||
return false;
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
}).then(function (result) {
|
||||
if (!result.isConfirmed) {
|
||||
return;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: url,
|
||||
type: 'PUT',
|
||||
headers: { 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json' },
|
||||
data: { komoditas_ids: result.value },
|
||||
success: function (res) {
|
||||
Swal.fire({ icon: 'success', title: 'Berhasil!', text: res.message, showConfirmButton: false, timer: 1500 })
|
||||
.then(() => location.reload());
|
||||
},
|
||||
error: function (xhr) {
|
||||
const res = xhr.responseJSON || {};
|
||||
const errors = res.errors ? Object.values(res.errors).flat().join('<br>') : null;
|
||||
Swal.fire({ icon: 'error', title: 'Gagal!', html: errors || res.message || 'Gagal menyimpan komoditas.' });
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
@@ -26,7 +26,8 @@
|
||||
$appName = env('APP_NAME');
|
||||
$isAdmin = in_array($userRole, ['super_admin', 'admin'], true);
|
||||
$isSuperAdmin = $userRole === 'super_admin';
|
||||
$canViewDashboard = $isAdmin || $userRole === 'fasilitator';
|
||||
$isKoordinator = $userRole === 'koordinator';
|
||||
$canViewDashboard = $isAdmin || $isKoordinator || $userRole === 'enumerator';
|
||||
@endphp
|
||||
|
||||
@if($canViewDashboard)
|
||||
@@ -36,7 +37,7 @@
|
||||
<div data-i18n="Dashboard">Dashboard</div>
|
||||
</a>
|
||||
|
||||
@if($isAdmin && $appName === 'AgroMonev')
|
||||
@if(($isAdmin || $isKoordinator) && $appName === 'AgroMonev')
|
||||
<ul class="menu-sub">
|
||||
<li class="menu-item {{ request()->routeIs(['dashboard.karakteristik-petani', 'dashboard.demografi-petani', 'dashboard.indeks-petani']) ? 'active' : '' }}">
|
||||
<a href="{{ route('dashboard.karakteristik-petani') }}" class="menu-link">
|
||||
|
||||
@@ -315,7 +315,7 @@
|
||||
<th>Komoditas Utama</th>
|
||||
<th>Luas Lahan</th>
|
||||
@if($showFasilitatorColumn)
|
||||
<th>Fasilitator</th>
|
||||
<th>Enumerator</th>
|
||||
@endif
|
||||
<th>Segmentasi</th>
|
||||
<th>Aksi</th>
|
||||
|
||||
@@ -105,7 +105,7 @@
|
||||
<p class="text-body-secondary small mb-0">Beberapa data belum tersedia. Lengkapi agar informasi profil dan penilaian lebih utuh.</p>
|
||||
</div>
|
||||
</div>
|
||||
@canany(['super_admin', 'admin', 'fasilitator'])
|
||||
@canany(['super_admin', 'admin', 'enumerator'])
|
||||
<a href="{{ route('petani.edit', $petani->id) }}" class="btn btn-sm btn-primary flex-shrink-0 profile-completeness-action">
|
||||
<i class="icon-base ti tabler-edit icon-sm me-2" aria-hidden="true"></i>Lengkapi data
|
||||
</a>
|
||||
|
||||
+11
-4
@@ -3,7 +3,6 @@
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
//Auth
|
||||
Route::put('/users/{id}/verify', [App\Http\Controllers\Web\Konfigurasi\UserController::class, 'verify'])->name('users.verify');
|
||||
Route::get('/login', [App\Http\Controllers\Web\Konfigurasi\AuthController::class, 'login'])->name('login');
|
||||
Route::get('/register', [App\Http\Controllers\Web\Konfigurasi\AuthController::class, 'register'])->name('register');
|
||||
Route::post('/create-account', [App\Http\Controllers\Web\Konfigurasi\AuthController::class, 'createAccount'])->name('auth.createAccount');
|
||||
@@ -17,10 +16,15 @@ Route::get('/', function() {
|
||||
return redirect()->route('login');
|
||||
}
|
||||
|
||||
if (auth()->user()->role->value === 'fasilitator') {
|
||||
if (auth()->user()->role->value === 'enumerator') {
|
||||
return redirect()->route('dashboard.survey-petani');
|
||||
}
|
||||
|
||||
// Fasilitator (pemateri workshop) belum punya menu khusus
|
||||
if (auth()->user()->role->value === 'fasilitator') {
|
||||
return redirect()->route('userProfile.index');
|
||||
}
|
||||
|
||||
return redirect()->route('dashboard.karakteristik-petani');
|
||||
})->name('home');
|
||||
|
||||
@@ -82,7 +86,8 @@ Route::middleware('auth')
|
||||
|
||||
// User
|
||||
Route::resource('users', App\Http\Controllers\Web\Konfigurasi\UserController::class);
|
||||
Route::patch('/users/{user}/verify', [App\Http\Controllers\Web\Konfigurasi\UserController::class, 'verify'])->name('users.verify');
|
||||
Route::put('/users/{user}/verify', [App\Http\Controllers\Web\Konfigurasi\UserController::class, 'verify'])->name('users.verify');
|
||||
Route::singleton('users.komoditas', App\Http\Controllers\Web\Konfigurasi\KoordinatorKomoditasController::class)->only(['update']);
|
||||
|
||||
// Provinsi
|
||||
Route::get('/provinsi/list', [App\Http\Controllers\Web\Master\ProvinsiController::class, 'list'])->name('provinsi.list');
|
||||
@@ -220,9 +225,10 @@ Route::middleware('auth')
|
||||
Route::resource('realisasi', App\Http\Controllers\Web\Program\RealisasiController::class)->only('index', 'store', 'show', 'update', 'destroy');
|
||||
Route::resource('verifikasi', App\Http\Controllers\Web\Program\VerifikasiController::class)->only('store', 'destroy');
|
||||
|
||||
// Petani (dibatasi per role & komoditas koordinator, lihat BatasiAksesPetani)
|
||||
Route::middleware('akses.petani')->group(function () {
|
||||
Route::get('petani/show-image/{id}/{field}', [App\Http\Controllers\Web\Produktivitas\PetaniController::class, 'showImage'])->name('petani.show-image');
|
||||
|
||||
// Petani
|
||||
Route::resource('petani', App\Http\Controllers\Web\Produktivitas\PetaniController::class)->whereNumber('petani');
|
||||
Route::get('petani/{petani}/akses-pasar', [App\Http\Controllers\Web\Produktivitas\AksesPasarController::class, 'editByPetani'])->name('petani.akses-pasar.edit');
|
||||
Route::get('petani/{petani}/lahan-tanaman/summary', [App\Http\Controllers\Web\Produktivitas\PetaniLahanController::class, 'summary'])->name('petani.lahan-tanaman.summary');
|
||||
@@ -243,6 +249,7 @@ Route::middleware('auth')
|
||||
Route::resource('petani.akses-pasar', App\Http\Controllers\Web\Produktivitas\AksesPasarController::class)->whereNumber('petani');
|
||||
|
||||
Route::resource('petani.skor', App\Http\Controllers\Web\Produktivitas\SkorPetaniController::class)->only(['index']);
|
||||
});
|
||||
|
||||
Route::resource('survey/kelompok-tani', App\Http\Controllers\Web\Produktivitas\SurveyKelompokTaniController::class)->except(['show'])->names('kelompok-tani')->parameters(['kelompok-tani' => 'kelompokTani']);
|
||||
Route::prefix('survey/kelompok-tani/{kelompokTani}')->name('kelompok-tani.')->group(function () {
|
||||
|
||||
Reference in New Issue
Block a user