integrate image cropper for avatar upload/delete option/retouch
This commit is contained in:
@@ -224,6 +224,13 @@ class PetaniController extends Controller
|
||||
|
||||
$validatedData = $this->validatePetani($request);
|
||||
|
||||
if ($request->input('hapus_foto') == '1') {
|
||||
if ($petani->foto && Storage::exists($petani->foto)) {
|
||||
Storage::delete($petani->foto);
|
||||
}
|
||||
$validatedData['foto'] = null;
|
||||
}
|
||||
|
||||
if ($request->hasFile('file_ktp')) {
|
||||
$file_ktp = $request->file('file_ktp');
|
||||
$validatedData['path_ktp'] = $file_ktp->store('petani/ktp');
|
||||
@@ -235,6 +242,10 @@ class PetaniController extends Controller
|
||||
$validatedData['file_kk'] = $file_kk->getClientOriginalName();
|
||||
}
|
||||
if ($request->hasFile('foto')) {
|
||||
|
||||
if ($petani->foto && Storage::exists($petani->foto)) {
|
||||
Storage::delete($petani->foto);
|
||||
}
|
||||
$file_foto = $request->file('foto');
|
||||
$validatedData['foto'] = $file_foto->store('petani/foto');
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 5.4 KiB |
@@ -1,3 +1,17 @@
|
||||
@push('style')
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/croppie/2.6.5/croppie.min.css" />
|
||||
|
||||
<style>
|
||||
#croppie-container {
|
||||
width: 100%;
|
||||
height: 350px;
|
||||
}
|
||||
</style>
|
||||
@endpush
|
||||
|
||||
@push('scripts')
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/croppie/2.6.5/croppie.min.js"></script>
|
||||
|
||||
@extends('layout.main')
|
||||
|
||||
@section('content')
|
||||
@@ -29,7 +43,7 @@
|
||||
<div class=" d-flex align-items-center flex-column">
|
||||
<img id="fotoPreview"
|
||||
class="img-fluid rounded mb-4"
|
||||
src="{{ isset($petani) && $petani?->foto ? asset('storage/' . $petani->foto) : asset('assets/img/avatars/1.png') }}"
|
||||
src="{{ isset($petani) && $petani?->foto ? asset('storage/' . $petani->foto) : asset('assets/img/avatars/3.jpg') }}"
|
||||
height="120" width="120"
|
||||
alt="Foto profil petani"
|
||||
data-bs-toggle="tooltip" data-bs-placement="top"
|
||||
@@ -230,6 +244,24 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="cropModal" tabindex="-1" aria-labelledby="cropModalLabel" aria-hidden="true" data-bs-backdrop="static" data-bs-keyboard="false">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="cropModalLabel">Sesuaikan Foto Profil</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="croppie-container"></div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Batal</button>
|
||||
<button type="button" class="btn btn-primary" id="cropImageBtn">Potong & Simpan</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
@@ -300,24 +332,74 @@
|
||||
}
|
||||
});
|
||||
$('#add-petani-desa').select2();
|
||||
|
||||
let croppieInstance = null;
|
||||
|
||||
$('#fotoPreview').on('click', function () {
|
||||
$('#foto').val('');
|
||||
$('#foto').click();
|
||||
});
|
||||
|
||||
$('#foto').on('change', function (e) {
|
||||
|
||||
const file = e.target.files[0];
|
||||
if (!file) return;
|
||||
|
||||
if(!file.type.match(/^image\/(png|jpeg|jpg)$/)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = function (event) {
|
||||
$('#fotoPreview').attr('src', event.target.result);
|
||||
$('#cropModal').modal('show');
|
||||
|
||||
if (!croppieInstance) {
|
||||
const el = document.getElementById('croppie-container');
|
||||
croppieInstance = new Croppie(el, {
|
||||
viewport: { width: 200, height: 200,},
|
||||
boundary: { width: 300, height: 300 },
|
||||
showZoomer: true,
|
||||
enableOrientation: true
|
||||
});
|
||||
}
|
||||
|
||||
setTimeout(function() {
|
||||
croppieInstance.bind({
|
||||
url: event.target.result
|
||||
});
|
||||
}, 200);
|
||||
};
|
||||
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
|
||||
$('#cropImageBtn').on('click', function() {
|
||||
croppieInstance.result({
|
||||
type: 'base64',
|
||||
format: 'png',
|
||||
size: 'viewport'
|
||||
}).then(function(base64) {
|
||||
|
||||
$('#fotoPreview').attr('src', base64);
|
||||
|
||||
function dataURLtoFile(dataurl, filename) {
|
||||
let arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
|
||||
bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
|
||||
while(n--){
|
||||
u8arr[n] = bstr.charCodeAt(n);
|
||||
}
|
||||
return new File([u8arr], filename, {type: mime});
|
||||
}
|
||||
|
||||
let croppedFile = dataURLtoFile(base64, 'foto_profil_crop.png');
|
||||
|
||||
let dataTransfer = new DataTransfer();
|
||||
dataTransfer.items.add(croppedFile);
|
||||
document.getElementById('foto').files = dataTransfer.files;
|
||||
|
||||
$('#cropModal').modal('hide');
|
||||
});
|
||||
});
|
||||
|
||||
// 1 MB = 1048576 bytes
|
||||
$.validator.addMethod('maxfilesize', function (value, element, param) {
|
||||
return this.optional(element) || (element.files[0] && element.files[0].size <= param);
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
@extends('layout.main')
|
||||
|
||||
@push('style')
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/croppie/2.6.5/croppie.min.css" />
|
||||
|
||||
<style>
|
||||
#croppie-container {
|
||||
width: 100%;
|
||||
height: 350px;
|
||||
}
|
||||
</style>
|
||||
@endpush
|
||||
|
||||
@push('scripts')
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/croppie/2.6.5/croppie.min.js"></script>
|
||||
|
||||
@section('content')
|
||||
<div class="container-xxl flex-grow-1 container-p-y">
|
||||
@if ($errors->any())
|
||||
@@ -23,20 +37,24 @@
|
||||
<form id="editPetaniForm" action="{{ route('petani.update', $petani->id) }}" method="POST" enctype="multipart/form-data">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
<input type="hidden" name="hapus_foto" id="hapus_foto" value="0">
|
||||
<input type="file" id="foto" name="foto" accept="image/png,image/jpeg,image/jpg" hidden>
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<div class="user-avatar-section">
|
||||
<div class=" d-flex align-items-center flex-column">
|
||||
<img id="fotoPreview"
|
||||
class="img-fluid rounded mb-4"
|
||||
src="{{ $petani->foto ? route('petani.show-image', ['id' => $petani->id, 'field' => 'foto']) : asset('assets/img/avatars/1.png') }}"
|
||||
height="120" width="120"
|
||||
<img id="fotoPreview"
|
||||
class="mb-3 shadow-sm rounded-3"
|
||||
src="{{ $petani->foto ? route('petani.show-image', ['id' => $petani->id, 'field' => 'foto']) : asset('assets/img/avatars/3.jpg') }}"
|
||||
alt="Foto profil petani"
|
||||
data-bs-toggle="tooltip" data-bs-placement="top"
|
||||
data-bs-original-title="Klik untuk mengganti foto profil petani"
|
||||
style="cursor:pointer; object-fit:cover;" />
|
||||
<div class="user-info text-center">
|
||||
style="width: 120px !important; height: 120px !important; cursor:pointer; object-fit:cover; display:block; margin: 0 auto;" />
|
||||
|
||||
<button type="button" id="btnHapusFoto" class="btn btn-sm btn-outline-danger mb-4" style="display: {{ $petani->foto ? 'block' : 'none' }};">
|
||||
Hapus Foto
|
||||
</button>
|
||||
<div class="user-info text-center">
|
||||
<h5><i>{{ $petani->nama }}</i></h5>
|
||||
<span class="badge bg-label-secondary">Petani {{ $petani->komoditas?->nama }}</span>
|
||||
</div>
|
||||
@@ -234,6 +252,24 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="cropModal" tabindex="-1" aria-labelledby="cropModalLabel" aria-hidden="true" data-bs-backdrop="static" data-bs-keyboard="false">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="cropModalLabel">Sesuaikan Foto Profil</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="croppie-container"></div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Batal</button>
|
||||
<button type="button" class="btn btn-primary" id="cropImageBtn">Potong & Simpan</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
@@ -306,24 +342,86 @@
|
||||
});
|
||||
$('#edit-petani-desa').select2();
|
||||
|
||||
let croppieInstance = null;
|
||||
|
||||
$('#fotoPreview').on('click', function () {
|
||||
$('#foto').val('');
|
||||
$('#foto').click();
|
||||
});
|
||||
|
||||
$('#foto').on('change', function (e) {
|
||||
|
||||
const file = e.target.files[0];
|
||||
if (!file) return;
|
||||
|
||||
if(!file.type.match(/^image\/(png|jpeg|jpg)$/)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = function (event) {
|
||||
$('#fotoPreview').attr('src', event.target.result);
|
||||
$('#cropModal').modal('show');
|
||||
|
||||
if (!croppieInstance) {
|
||||
const el = document.getElementById('croppie-container');
|
||||
croppieInstance = new Croppie(el, {
|
||||
viewport: { width: 200, height: 200 },
|
||||
boundary: { width: '100%', height: 280 },
|
||||
showZoomer: true,
|
||||
enableOrientation: true
|
||||
});
|
||||
}
|
||||
|
||||
setTimeout(function() {
|
||||
croppieInstance.bind({
|
||||
url: event.target.result
|
||||
});
|
||||
}, 200);
|
||||
};
|
||||
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
|
||||
// 1 MB = 1048576 bytes
|
||||
|
||||
$('#cropImageBtn').on('click', function() {
|
||||
croppieInstance.result({
|
||||
type: 'base64',
|
||||
format: 'png',
|
||||
size: 'viewport',
|
||||
}).then(function(base64) {
|
||||
|
||||
$('#fotoPreview').attr('src', base64);
|
||||
|
||||
$('#hapus_foto').val('0');
|
||||
$('#btnHapusFoto').show();
|
||||
|
||||
function dataURLtoFile(dataurl, filename) {
|
||||
let arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
|
||||
bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
|
||||
while(n--){
|
||||
u8arr[n] = bstr.charCodeAt(n);
|
||||
}
|
||||
return new File([u8arr], filename, {type: mime});
|
||||
}
|
||||
|
||||
let croppedFile = dataURLtoFile(base64, 'foto_profil_crop.png');
|
||||
|
||||
let dataTransfer = new DataTransfer();
|
||||
dataTransfer.items.add(croppedFile);
|
||||
document.getElementById('foto').files = dataTransfer.files;
|
||||
|
||||
$('#cropModal').modal('hide');
|
||||
});
|
||||
});
|
||||
|
||||
$('#btnHapusFoto').on('click', function() {
|
||||
$('#hapus_foto').val('1');
|
||||
|
||||
$('#foto').val('');
|
||||
|
||||
$('#fotoPreview').attr('src', "{{ asset('assets/img/avatars/3.jpg') }}");
|
||||
|
||||
$(this).hide();
|
||||
});
|
||||
$.validator.addMethod('maxfilesize', function (value, element, param) {
|
||||
return this.optional(element) || (element.files[0] && element.files[0].size <= param);
|
||||
}, 'File size must be less than {0} bytes.');
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
<div class="d-flex justify-content-start align-items-center mb-3">
|
||||
<div class="avatar me-2">
|
||||
@if (!isset($petani))
|
||||
<img src="{{ asset('assets/img/avatars/1.png') }}" alt="Foto Petani" class="rounded-circle" />
|
||||
<img src="{{ asset('assets/img/avatars/3.jpg') }}" alt="Foto Petani"/>
|
||||
@else
|
||||
<img class="rounded-circle" alt="Foto Petani"
|
||||
src="{{ $petani->foto ? route('petani.show-image', ['id' => $petani->id, 'field' => 'foto']) : asset('assets/img/avatars/1.png') }}" />
|
||||
<img alt="Foto Petani"
|
||||
src="{{ $petani->foto ? route('petani.show-image', ['id' => $petani->id, 'field' => 'foto']) : asset('assets/img/avatars/3.jpg') }}" class="rounded-3"/>
|
||||
@endif
|
||||
</div>
|
||||
<div class="d-flex flex-column">
|
||||
|
||||
Reference in New Issue
Block a user