From 3e1d3a33eed333deec19540659077acc5919d137 Mon Sep 17 00:00:00 2001 From: althaf Date: Mon, 20 Jul 2026 16:36:12 +0700 Subject: [PATCH] fix: resolve existing photos preview in Dropzone Lahan&Tanaman --- .../Api/Produktivitas/LahanController.php | 44 +++- .../Produktivitas/PetaniLahanController.php | 33 ++- .../produktivitas/petani/lahan/add.blade.php | 41 ++- .../produktivitas/petani/lahan/edit.blade.php | 241 +++++++++++++++++- 4 files changed, 327 insertions(+), 32 deletions(-) diff --git a/app/Http/Controllers/Api/Produktivitas/LahanController.php b/app/Http/Controllers/Api/Produktivitas/LahanController.php index 8a3e12e..6ccd277 100644 --- a/app/Http/Controllers/Api/Produktivitas/LahanController.php +++ b/app/Http/Controllers/Api/Produktivitas/LahanController.php @@ -16,7 +16,7 @@ use App\Enums\StatusLahan; class LahanController extends Controller { - /** + /** * Display a listing of the resource. */ public function index(Request $request): AnonymousResourceCollection @@ -97,11 +97,12 @@ class LahanController extends Controller 'nama' => ['required', 'string', 'max:255'], 'status_kepemilikan' => ['required', new Enum(StatusLahan::class)], 'desa_kelurahan_id' => ['nullable', 'numeric', 'exists:master_desa_kelurahan,id'], - 'path' => ['nullable', 'array'], - 'path.*' => ['array'], + 'path' => ['nullable', 'string'], 'luas_lahan' => ['nullable', 'numeric'], 'uid' => ['required', 'string'], 'profile_id' => $profileRule, + 'foto' => ['nullable', 'array'], + 'foto.*' => ['image', 'mimes:jpeg,png,jpg', 'max:5120'], // Max 5MB ]); $targetProfileId = $user->role === 'fasilitator' @@ -120,10 +121,22 @@ class LahanController extends Controller ? $profile->desa_kelurahan_id : ($validated['desa_kelurahan_id'] ?? $profile->desa_kelurahan_id); + $fotoPaths = []; + if ($request->hasFile('foto')) { + foreach ($request->file('foto') as $file) { + $fotoPaths[] = $file->store('foto_lahan', 'public'); + } + } + $lahan = Lahan::create([ - ...$validated, - 'profile_id' => $profile->id, + 'nama' => $validated['nama'], + 'status_kepemilikan' => $validated['status_kepemilikan'], 'desa_kelurahan_id' => $desaKelurahanId, + 'path' => $validated['path'] ?? null, + 'luas_lahan' => $validated['luas_lahan'] ?? null, + 'uid' => $validated['uid'], + 'profile_id' => $profile->id, + 'foto' => $fotoPaths, ]); return (new LahanResource($lahan)) @@ -153,7 +166,7 @@ class LahanController extends Controller /** * Update the specified resource in storage. */ - public function update(Request $request, string $id): JsonResponse + public function update(Request $request, string $id): JsonResponse { if (!Gate::any(['petani', 'fasilitator','super_admin'])) { abort(403); @@ -173,9 +186,26 @@ class LahanController extends Controller 'path' => ['nullable', 'array'], 'path.*' => ['array'], 'luas_lahan' => ['nullable', 'numeric'], + 'foto' => ['nullable', 'array'], + 'foto.*' => ['image', 'mimes:jpeg,png,jpg', 'max:5120'], ]); - $lahan->update($validated); + $fotoPaths = $lahan->foto ?? []; + + if ($request->hasFile('foto')) { + foreach ($request->file('foto') as $file) { + $fotoPaths[] = $file->store('foto_lahan', 'public'); + } + } + + $lahan->update([ + 'nama' => $validated['nama'], + 'status_kepemilikan' => $validated['status_kepemilikan'], + 'desa_kelurahan_id' => $validated['desa_kelurahan_id'] ?? $lahan->desa_kelurahan_id, + 'path' => $validated['path'] ?? null, + 'luas_lahan' => $validated['luas_lahan'] ?? null, + 'foto' => $fotoPaths, + ]); return (new LahanResource($lahan))->response(); } diff --git a/app/Http/Controllers/Web/Produktivitas/PetaniLahanController.php b/app/Http/Controllers/Web/Produktivitas/PetaniLahanController.php index c4febdb..873bbba 100644 --- a/app/Http/Controllers/Web/Produktivitas/PetaniLahanController.php +++ b/app/Http/Controllers/Web/Produktivitas/PetaniLahanController.php @@ -256,9 +256,28 @@ class PetaniLahanController extends Controller $tanamanCount = Tanaman::where('lahan_id', $lahan->id)->count(); $lahanCount = Lahan::where('petani_id', $petaniId)->count(); + $fotoLama = $lahan->foto ?? []; + if (is_string($fotoLama)) { + $fotoLama = json_decode($fotoLama, true); + } + if (!is_array($fotoLama)) { + $fotoLama = []; + } + + if ($request->hasFile('foto')) { + foreach ($request->file('foto') as $file) { + $path = $file->store('lahan', 'public'); + $fotoLama[] = [ + 'path' => $path, + 'file' => $file->getClientOriginalName(), + ]; + } + } + + unset($validated['foto']); + $lahan->update([...$validated, - //'nama' => 'Lahan ' . $komoditas->nama . ' #' . ($lahanCount + 1), - //'petani_id' => $petaniId, + 'foto' => count($fotoLama) > 0 ? $fotoLama : null, 'updated_by' => auth()->id()] ); @@ -293,7 +312,15 @@ class PetaniLahanController extends Controller }); } - return redirect()->route('petani.lahan-tanaman.index', $petaniId)->with('success', 'Lahan berhasil ditambahkan.'); + if ($request->ajax()) { + return response()->json([ + 'success' => true, + 'redirect' => route('petani.lahan-tanaman.index', $petaniId), + 'message' => 'Lahan berhasil diupdate.' + ]); + } + + return redirect()->route('petani.lahan-tanaman.index', $petaniId)->with('success', 'Lahan berhasil diperbarui.'); } public function destroy(string $id) diff --git a/resources/views/produktivitas/petani/lahan/add.blade.php b/resources/views/produktivitas/petani/lahan/add.blade.php index 5103d90..dc8b203 100644 --- a/resources/views/produktivitas/petani/lahan/add.blade.php +++ b/resources/views/produktivitas/petani/lahan/add.blade.php @@ -389,7 +389,7 @@ - + @error('file')
{{ $message }}
@enderror @@ -759,8 +759,6 @@ tempInput.click(); } }); - // ----------------------------------- - // FORM $('#petani-lahan-tanaman').validate({ rules: { @@ -805,21 +803,44 @@ submitHandler: function (form) { const formData = new FormData(form); - const dz = $("#dropzone-foto")[0].dropzone; + if (myDropzone) { + myDropzone.files.forEach(function(file) { + formData.append("foto[]", file); + }); + } - dz.getAcceptedFiles().forEach(function(file) { - formData.append("foto[]", file); - }); + let submitBtn = $(form).find('button[type="submit"]'); + let originalText = submitBtn.html(); + submitBtn.prop('disabled', true).html(' Menyimpan...'); $.ajax({ - url: form.action, + url: $(form).attr('action'), type: "POST", data: formData, processData: false, contentType: false, + headers: { + 'X-CSRF-TOKEN': $('input[name="_token"]').val(), + 'Accept': 'application/json' + }, success: function(res) { - if (res.redirect) { - window.location.href = res.redirect; + alert('Data lahan berhasil ditambahkan!'); + window.location.href = "{{ route('petani.index') }}"; + }, + error: function(xhr) { + submitBtn.prop('disabled', false).html(originalText); + + if (xhr.status === 422) { + let errors = xhr.responseJSON.errors; + let errorMsg = ""; + $.each(errors, function(key, val) { + errorMsg += val[0] + "\n"; + }); + alert("Penyimpanan Gagal:\n\n" + errorMsg); + } else { + let errorDetail = xhr.responseJSON && xhr.responseJSON.message ? xhr.responseJSON.message : xhr.statusText; + alert("Error " + xhr.status + ": " + errorDetail); + console.error(xhr.responseText); } } }); diff --git a/resources/views/produktivitas/petani/lahan/edit.blade.php b/resources/views/produktivitas/petani/lahan/edit.blade.php index 2d4a280..676fcf4 100644 --- a/resources/views/produktivitas/petani/lahan/edit.blade.php +++ b/resources/views/produktivitas/petani/lahan/edit.blade.php @@ -3,6 +3,7 @@ @push('style') + @endpush @@ -224,9 +247,8 @@
- - Gunakan tombol edit (pensil) atau hapus (tong sampah) di peta untuk mengubah bentuk lahan. + @error('path')
{{ $message }}
@enderror
@@ -243,6 +265,24 @@ +
+
+
+
Foto
+
+
+ + Tambah Foto +
+
+ +
+ + + @error('file')
{{ $message }}
@enderror +
+
+
Kembali @@ -358,6 +398,7 @@ @endsection @push('scripts') + @@ -546,7 +587,7 @@ let regionBoundaryLayer = L.featureGroup().addTo(map); function focusToRegionBoundary(selectElement, shouldZoom = true) { - regionBoundaryLayer.clearLayers(); // Bersihkan garis wilayah sebelumnya + regionBoundaryLayer.clearLayers(); let selectedOption = selectElement.options[selectElement.selectedIndex]; if (!selectedOption || !selectedOption.hasAttribute('data-path')) return; @@ -561,7 +602,7 @@ function formatCoordinates(coords) { if (coords.length === 2 && typeof coords[0] === 'number') { - return [coords[0], coords[1]]; // JANGAN DIBALIK + return [coords[0], coords[1]]; } return coords.map(formatCoordinates); } @@ -637,10 +678,8 @@ map.on(L.Draw.Event.CREATED, function (e) { let layer = e.layer; - drawnItems.clearLayers(); drawnItems.addLayer(layer); - updatePathTextarea(layer); }); @@ -660,16 +699,143 @@ return [latlng.lng, latlng.lat]; }); document.getElementById('path').value = JSON.stringify(pathData); - let area = L.GeometryUtil.geodesicArea(latlngs); - - console.log(area); - $('#luas_lahan').val(area.toFixed(2)); } - // FORM + const previewTemplate = ` +
+ +
+
+
+
+
+
`; + Dropzone.autoDiscover = false; + let myDropzone = null; + const dropzoneElement = document.querySelector("#dropzone-container"); + + if (dropzoneElement) { + myDropzone = new Dropzone(dropzoneElement, { + url: "/", + autoProcessQueue: false, + previewTemplate: previewTemplate, + clickable: "#btn-tambah-foto", + previewsContainer: "#dropzone-container", + parallelUploads: 10, + maxFilesize: 5, + addRemoveLinks: false, + acceptedFiles: "image/jpeg,image/jpg,image/png", + + init: function () { + let dz = this; + + let rawPhotos = {!! json_encode($lahanSelected->foto ?? []) !!}; + let existingPhotos = []; + + if (typeof rawPhotos === 'string') { + try { + existingPhotos = JSON.parse(rawPhotos); + } catch(e) { + existingPhotos = []; + } + } else if (Array.isArray(rawPhotos)) { + existingPhotos = rawPhotos; + } + + if (existingPhotos && existingPhotos.length > 0) { + existingPhotos.forEach(function (photo, index) { + + let pathStr = typeof photo === 'object' ? (photo.path || photo[0] || '') : photo; + + if (pathStr && pathStr !== '') { + let mockFile = { + name: "Foto Lahan " + (index + 1), + size: 1024, + id: index, + isServerFile: true, + accepted: true + }; + + dz.emit("addedfile", mockFile); + dz.emit("thumbnail", mockFile, "{{ asset('storage') }}/" + pathStr); + dz.emit("complete", mockFile); + + dz.files.push(mockFile); + } + }); + } + + this.on("removedfile", function (file) { + if (file.isServerFile) { + let deleteInput = $('').attr({ + type: 'hidden', + name: 'deleted_photos[]', + value: file.id + }); + $('#deleted-photos-container').append(deleteInput); + } + }); + } + }); + } + + $(document).on('click', '.btn-edit', function(e) { + e.preventDefault(); + e.stopPropagation(); + + let previewElement = $(this).closest('.dz-preview')[0]; + let dz = myDropzone; + let fileToReplace = dz.files.find(f => f.previewElement === previewElement); + + if (fileToReplace) { + let tempInput = document.createElement('input'); + tempInput.type = 'file'; + tempInput.accept = 'image/jpeg,image/jpg,image/png'; + + tempInput.onchange = function(event) { + let newFile = event.target.files[0]; + if (newFile) { + if(fileToReplace.isServerFile){ + let deleteInput = $('').attr({ + type: 'hidden', + name: 'deleted_photos[]', + value: fileToReplace.id + }); + $('#deleted-photos-container').append(deleteInput); + } + + dz.removeFile(fileToReplace); + dz.addFile(newFile); + } + }; + tempInput.click(); + } + }); + + $(document).on('click', '#dropzone-container .dz-image img', function (e) { + e.stopPropagation(); + $('#foto-lightbox-img').attr('src', this.src); + $('#foto-lightbox').addClass('active'); + }); + + $('#foto-lightbox').on('click', function (e) { + if (e.target === this || $(e.target).hasClass('foto-lightbox-close')) { + $(this).removeClass('active'); + } + }); + + + // FORM VALIDATION & SUBMIT $('#petani-lahan-tanaman').validate({ rules: { luas_lahan: { required: true }, @@ -697,6 +863,57 @@ }, unhighlight: function (element) { $(element).removeClass('is-invalid'); + }, + submitHandler: function (form) { + const formData = new FormData(form); + + formData.append('_method', 'PUT'); + + if (myDropzone) { + myDropzone.files.forEach(function(file) { + if (!file.isServerFile) { + formData.append("foto[]", file); + } + }); + } + + let submitBtn = $(form).find('button[type="submit"]'); + let originalText = submitBtn.html(); + submitBtn.prop('disabled', true).html(' Menyimpan...'); + + $.ajax({ + url: $(form).attr('action'), + type: "POST", + data: formData, + processData: false, + contentType: false, + headers: { + 'X-CSRF-TOKEN': $('input[name="_token"]').val(), + 'Accept': 'application/json' + }, + success: function(res) { + alert('Data lahan berhasil disimpan!'); + window.location.href = "{{ route('petani.index') }}"; + }, + error: function(xhr) { + submitBtn.prop('disabled', false).html(originalText); + + if (xhr.status === 422) { + let errors = xhr.responseJSON.errors; + let errorMsg = ""; + $.each(errors, function(key, val) { + errorMsg += val[0] + "\n"; + }); + alert("Penyimpanan Gagal:\n\n" + errorMsg); + } else { + let errorDetail = xhr.responseJSON && xhr.responseJSON.message ? xhr.responseJSON.message : xhr.statusText; + alert("Error " + xhr.status + ": " + errorDetail); + console.error(xhr.responseText); + } + } + }); + + return false; } });