From 298c591a2021157f34c085694a6e2d8fa4f47249 Mon Sep 17 00:00:00 2001 From: Irwan Cahyono Date: Mon, 13 Apr 2026 11:53:45 +0700 Subject: [PATCH] profile dan lahan --- app/Enums/Role.php | 2 +- app/Http/Controllers/Api/LahanController.php | 76 ++++++++--- .../Controllers/Api/ProfileController.php | 124 ++++++++++++++++-- app/Http/Controllers/Web/AuthController.php | 2 +- public/assets/js/pages-auth.js | 20 +-- resources/views/auth/login.blade.php | 35 ++--- routes/api.php | 2 +- 7 files changed, 196 insertions(+), 65 deletions(-) diff --git a/app/Enums/Role.php b/app/Enums/Role.php index fae2c16..c2f2118 100644 --- a/app/Enums/Role.php +++ b/app/Enums/Role.php @@ -5,7 +5,7 @@ enum Role: string { case SUPER_ADMIN = 'super_admin'; - case ADMIN = 'admin'; + case ADMIN = 'admin'; // admin program case FASILITATOR = 'fasilitator'; case PETANI = 'petani'; } diff --git a/app/Http/Controllers/Api/LahanController.php b/app/Http/Controllers/Api/LahanController.php index c4e45f7..29282cd 100644 --- a/app/Http/Controllers/Api/LahanController.php +++ b/app/Http/Controllers/Api/LahanController.php @@ -21,18 +21,22 @@ class LahanController extends Controller */ public function index(Request $request): AnonymousResourceCollection { - Gate::authorize('petani'); + if (!Gate::any(['petani', 'fasilitator'])) { + abort(403); + } $user = Auth::user(); $profile = $user->profile; + $profileId = $user->role === 'petani' ? $profile->id : $request->profile_id; + $size = $request->integer('size') ?: 10; - $listLahan = Lahan::where('profile_id', $profile->id) - ->when($request->search, function ($q, $search) { + $listLahan = Lahan::when($request->search, function ($q, $search) { $search = strtolower($search); $q->whereRaw('lower(nama) like ?', ["%{$search}%"]); }) + ->when($profileId, fn($q,$v) => $q->where('profile_id',$v)) ->when($request->status_kepemilikan, fn($q,$v) => $q->where('status_kepemilikan',$v)) ->when($request->desa_kelurahan_id, fn($q,$v) => $q->where('desa_kelurahan_id',$v)) ->when($request->kecamatan_id, fn ($q, $v) => @@ -46,7 +50,7 @@ public function index(Request $request): AnonymousResourceCollection ) ) ->when($request->provinsi_id, fn ($q, $v) => - $q->whereHas('desaKelurahan.kecamatan.kabupatenkota', fn ($k) => + $q->whereHas('desaKelurahan.kecamatan.kabupatenota', fn ($k) => $k->where('provinsi_id', $v) ) ); @@ -65,7 +69,6 @@ public function index(Request $request): AnonymousResourceCollection $listLahan = $listLahan->paginate($size); - return LahanResource::collection($listLahan); } @@ -74,11 +77,19 @@ public function index(Request $request): AnonymousResourceCollection */ public function store(Request $request): JsonResponse { - Gate::authorize('petani'); + if (!Gate::any(['petani', 'fasilitator'])) { + abort(403); + } $user = Auth::user(); $profile = $user->profile; + if (!$profile) { + abort(404, 'Profile tidak ditemukan'); + } + + $profileRule = $user->role === 'fasilitator' ? ['required', 'numeric', 'exists:profiles,id'] : ['nullable']; + $validated = $request->validate([ 'nama' => ['required', 'string', 'max:255'], 'status_kepemilikan' => ['required', new Enum(StatusLahan::class)], @@ -86,11 +97,29 @@ public function store(Request $request): JsonResponse 'path' => ['nullable', 'array'], 'path.*' => ['array'], 'luas_lahan' => ['nullable', 'numeric'], + 'profile_id' => $profileRule, ]); + $targetProfileId = $user->role === 'fasilitator' + ? $validated['profile_id'] + : $profile->id; + + if ($user->role === 'fasilitator') { + $targetProfile = Profile::findOrFail($targetProfileId); + + if ($targetProfile->desa_kelurahan_id !== $profile->desa_kelurahan_id) { + abort(403, 'Tidak boleh lintas desa'); + } + } + + $desaKelurahanId = $user->role === 'fasilitator' + ? $profile->desa_kelurahan_id + : ($validated['desa_kelurahan_id'] ?? $profile->desa_kelurahan_id); + $lahan = Lahan::create([ ...$validated, 'profile_id' => $profile->id, + 'desa_kelurahan_id' => $desaKelurahanId, ]); return (new LahanResource($lahan)) @@ -103,12 +132,16 @@ public function store(Request $request): JsonResponse */ public function show(string $id): JsonResponse { - Gate::authorize('petani'); + if (!Gate::any(['petani', 'fasilitator'])) { + abort(403); + } $lahan = Lahan::with('desaKelurahan')->findOrFail($id); - if ($lahan->profile_id !== Auth::user()->profile->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) { + return response()->json(['message' => 'Unauthorized'], 403); + } return (new LahanResource($lahan))->response(); } @@ -118,12 +151,16 @@ public function show(string $id): JsonResponse */ public function update(Request $request, string $id): JsonResponse { - Gate::authorize('petani'); + if (!Gate::any(['petani', 'fasilitator'])) { + abort(403); + } $lahan = Lahan::findOrFail($id); - if ($lahan->profile_id !== Auth::user()->profile->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) { + return response()->json(['message' => 'Unauthorized'], 403); + } $validated = $request->validate([ 'nama' => ['required', 'string', 'max:255'], @@ -144,12 +181,16 @@ public function update(Request $request, string $id): JsonResponse */ public function destroy(string $id): JsonResponse { - Gate::authorize('petani'); + if (!Gate::any(['petani', 'fasilitator'])) { + abort(403); + } $lahan = Lahan::findOrFail($id); - if ($lahan->profile_id !== Auth::user()->profile->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) { + return response()->json(['message' => 'Unauthorized'], 403); + } $lahan->delete(); @@ -158,7 +199,10 @@ public function destroy(string $id): JsonResponse public function batchUpsert(Request $request): JsonResponse { - Gate::authorize('petani'); + if (!Gate::any(['petani', 'fasilitator'])) { + abort(403); + } + $user = Auth::user(); $profile = $user->profile; diff --git a/app/Http/Controllers/Api/ProfileController.php b/app/Http/Controllers/Api/ProfileController.php index f59ebfe..d5175a8 100644 --- a/app/Http/Controllers/Api/ProfileController.php +++ b/app/Http/Controllers/Api/ProfileController.php @@ -4,12 +4,16 @@ use App\Http\Controllers\Controller; use Illuminate\Http\Request; -use App\Models\Profile; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\DB; use Illuminate\Http\JsonResponse; use Symfony\Component\HttpFoundation\BinaryFileResponse; +use Illuminate\Support\Facades\Gate; +use Illuminate\Validation\Rule; +use Illuminate\Support\Facades\Hash; +use App\Models\Profile; +use App\Models\User; use App\Http\Resources\ProfileResource; class ProfileController extends Controller @@ -17,9 +21,56 @@ class ProfileController extends Controller /** * Display a listing of the resource. */ - public function index() + public function index(Request $request) { - // + Gate::authorize('fasilitator'); + + $user = Auth::user(); + $profile = $user->profile; + + $desaKelurahanId = null; + if ($profile->role === 'fasilitator') { + $desaKelurahanId = $profile->desa_kelurahan_id; + } + + $size = $request->integer('size') ?: 10; + + $listProfile = Profile::when($request->search, function ($q, $search) { + $search = strtolower($search); + $q->whereRaw('lower(nama) like ?', ["%{$search}%"]); + }) + ->when($desaKelurahanId ?? $request->desa_kelurahan_id, fn($q,$v) => $q->where('desa_kelurahan_id',$v)) + ->when($request->kecamatan_id, fn ($q, $v) => + $q->whereHas('desaKelurahan', fn ($k) => + $k->where('kecamatan_id', $v) + ) + ) + ->when($request->kabupaten_kota_id, fn ($q, $v) => + $q->whereHas('desaKelurahan.kecamatan', fn ($k) => + $k->where('kabupaten_kota_id', $v) + ) + ) + ->when($request->provinsi_id, fn ($q, $v) => + $q->whereHas('desaKelurahan.kecamatan.kabupatenKota', fn ($k) => + $k->where('provinsi_id', $v) + ) + ); + if ($request->filled('sort')) { + $dir = str_starts_with($request->sort, '-') ? 'desc' : 'asc'; + $column = ltrim($request->sort, '-'); + + $allowed = ['id', 'nama']; + + if (in_array($column, $allowed)) { + $listProfile->orderBy($column, $dir); + } + } else { + $listProfile->orderBy('nama', 'asc'); + } + + $listProfile = $listProfile->paginate($size); + + return ProfileResource::collection($listProfile); } /** @@ -35,7 +86,40 @@ public function create() */ public function store(Request $request) { - // + Gate::authorize('fasilitator'); + + $validated = $request->validate([ + 'nama' => ['required', 'string', 'max:255'], + 'email' => ['required', 'email', 'max:255', 'unique:users,email'], + 'telepon' => ['nullable', 'string'], + 'alamat' => ['nullable', 'string'], + 'kk' => ['nullable', 'string'], + 'ktp' => ['nullable', 'string'], + 'file_kk' => ['nullable', 'file', 'mimes:jpg,jpeg,png,pdf', 'max:2048'], + 'file_ktp' => ['nullable', 'file', 'mimes:jpg,jpeg,png,pdf', 'max:2048'], + 'desa_kelurahan_id' => ['nullable', 'exists:master_desa_kelurahan,id'], + ]); + + $user = User::create([ + 'name' => $validated['nama'], + 'email' => $validated['email'], + 'password' => Hash::make('password123'), // Set default password or generate random + 'role' => 'petani', + 'email_verified_at' => now(), + 'created_by' => Auth::id(), + 'updated_by' => Auth::id(), + ]); + + $profile = Profile::create([ + ...$validated, + 'user_id' => $user->id, + 'created_by' => Auth::id(), + 'updated_by' => Auth::id(), + ]); + + return (new ProfileResource($profile)) + ->response() + ->setStatusCode(201); } /** @@ -43,6 +127,10 @@ public function store(Request $request) */ public function show(string $id): JSONResponse { + if (!Gate::any(['petani', 'fasilitator'])) { + abort(403); + } + $profile = Profile::with('desaKelurahan')->where('user_id', Auth::id())->first(); return response()->json(new ProfileResource($profile)); } @@ -60,9 +148,26 @@ public function edit(string $id) */ public function update(Request $request, string $id): JSONResponse { + if (!Gate::any(['petani', 'fasilitator'])) { + abort(403); + } + + $user = Auth::user(); + if ($user->role === 'petani' && $user->profile->id != $id) { + abort(403, 'Unauthorized'); + } + + $profile = Profile::findOrFail($id); + + if ($user->role === 'fasilitator' && $profile->desa_kelurahan_id != $user->profile->desa_kelurahan_id) { + abort(403, 'Unauthorized'); + } + + $emailRule = Rule::unique('users', 'email')->ignore($profile->user_id); + $validated = $request->validate([ 'nama' => ['required', 'string', 'max:255'], - 'email' => ['required', 'email', 'max:255', 'unique:users,email,' . Auth::id()], + 'email' => ['required', 'email', 'max:255', $emailRule], 'telepon' => ['nullable', 'string'], 'alamat' => ['nullable', 'string'], 'kk' => ['nullable', 'string'], @@ -72,8 +177,6 @@ public function update(Request $request, string $id): JSONResponse 'desa_kelurahan_id' => ['nullable', 'exists:master_desa_kelurahan,id'], ]); - $profile = Profile::where('user_id', Auth::id())->first(); - if ($request->hasFile('file_kk')) { $validated['file_kk'] = $request->file('file_kk')->getClientOriginalName(); $kkPath = $request->file('file_kk')->store('uploads/profile/' . $profile->id, 'public'); @@ -84,9 +187,8 @@ public function update(Request $request, string $id): JSONResponse $ktpPath = $request->file('file_ktp')->store('uploads/profile/' . $profile->id, 'public'); $validated['path_ktp'] = $ktpPath; } - $validated['updated_by'] = Auth::id(); - $user = Auth::user(); + $validated['updated_by'] = $user->id(); DB::transaction(function () use ($profile, $user, $validated) { $profile->update($validated); @@ -110,6 +212,10 @@ public function destroy(string $id) public function downloadFile(string $id, string $type): JsonResponse | BinaryFileResponse { + if (!Gate::any(['petani', 'fasilitator'])) { + abort(403); + } + $profile = Profile::where('user_id', Auth::id())->first(); if (!$profile) { diff --git a/app/Http/Controllers/Web/AuthController.php b/app/Http/Controllers/Web/AuthController.php index 300e843..d7abbd8 100644 --- a/app/Http/Controllers/Web/AuthController.php +++ b/app/Http/Controllers/Web/AuthController.php @@ -76,7 +76,7 @@ public function authenticate(Request $request) ? 'email' : 'name'; - if (Auth::attempt([$field => $login, 'password' => $request->password])) { + if (Auth::attempt([$field => $login, 'password' => $request->password], $request->boolean('remember'))) { if (Auth::user()->email_verified_at === null) { Auth::logout(); diff --git a/public/assets/js/pages-auth.js b/public/assets/js/pages-auth.js index 6783a5f..5dda75a 100644 --- a/public/assets/js/pages-auth.js +++ b/public/assets/js/pages-auth.js @@ -35,23 +35,23 @@ document.addEventListener('DOMContentLoaded', function () { 'email-username': { validators: { notEmpty: { - message: 'Please enter email / username' + message: 'Masukkan email atau username Anda' }, - stringLength: { - min: 6, - message: 'Username must be more than 6 characters' - } + // stringLength: { + // min: 6, + // message: 'Username must be more than 6 characters' + // } } }, password: { validators: { notEmpty: { - message: 'Please enter your password' + message: 'Masukkan password Anda' }, - stringLength: { - min: 6, - message: 'Password must be more than 6 characters' - } + // stringLength: { + // min: 6, + // message: 'Password must be more than 6 characters' + // } } }, 'confirm-password': { diff --git a/resources/views/auth/login.blade.php b/resources/views/auth/login.blade.php index 291f84f..4eda5f9 100644 --- a/resources/views/auth/login.blade.php +++ b/resources/views/auth/login.blade.php @@ -23,8 +23,8 @@ -

Welcome to AgroMonev! 👋

-

Please sign-in to your account and start the adventure

+

Selamat datang di AgroMonev!

+

Silakan masuk ke akun Anda

@if ($errors->any())
@@ -76,33 +76,14 @@ class="form-control"

- New on our platform? + Anda belum terdaftar? - Create an account + Buat akun baru

-
-
or
-
-
- - - - - - - - - - - - - - - -
+ @endsection \ No newline at end of file diff --git a/routes/api.php b/routes/api.php index 9e56a1d..b33212b 100644 --- a/routes/api.php +++ b/routes/api.php @@ -21,7 +21,7 @@ // Route::get('/profile', [\App\Http\Controllers\Api\ProfileController::class, 'show']); Route::put('/profile/{id}', [\App\Http\Controllers\Api\ProfileController::class, 'update']); Route::get('/profile/{id}/download/{type}', [\App\Http\Controllers\Api\ProfileController::class, 'downloadFile']); - Route::apiResource('/profile', \App\Http\Controllers\Api\ProfileController::class)->only(['show', 'update']); + Route::apiResource('/profile', \App\Http\Controllers\Api\ProfileController::class)->except(['create', 'edit', 'destroy']); }); Route::middleware('auth:sanctum')