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);
|
$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')) {
|
if ($request->hasFile('file_ktp')) {
|
||||||
$file_ktp = $request->file('file_ktp');
|
$file_ktp = $request->file('file_ktp');
|
||||||
$validatedData['path_ktp'] = $file_ktp->store('petani/ktp');
|
$validatedData['path_ktp'] = $file_ktp->store('petani/ktp');
|
||||||
@@ -235,6 +242,10 @@ class PetaniController extends Controller
|
|||||||
$validatedData['file_kk'] = $file_kk->getClientOriginalName();
|
$validatedData['file_kk'] = $file_kk->getClientOriginalName();
|
||||||
}
|
}
|
||||||
if ($request->hasFile('foto')) {
|
if ($request->hasFile('foto')) {
|
||||||
|
|
||||||
|
if ($petani->foto && Storage::exists($petani->foto)) {
|
||||||
|
Storage::delete($petani->foto);
|
||||||
|
}
|
||||||
$file_foto = $request->file('foto');
|
$file_foto = $request->file('foto');
|
||||||
$validatedData['foto'] = $file_foto->store('petani/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')
|
@extends('layout.main')
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
@@ -29,7 +43,7 @@
|
|||||||
<div class=" d-flex align-items-center flex-column">
|
<div class=" d-flex align-items-center flex-column">
|
||||||
<img id="fotoPreview"
|
<img id="fotoPreview"
|
||||||
class="img-fluid rounded mb-4"
|
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"
|
height="120" width="120"
|
||||||
alt="Foto profil petani"
|
alt="Foto profil petani"
|
||||||
data-bs-toggle="tooltip" data-bs-placement="top"
|
data-bs-toggle="tooltip" data-bs-placement="top"
|
||||||
@@ -230,6 +244,24 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</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
|
@endsection
|
||||||
|
|
||||||
@push('scripts')
|
@push('scripts')
|
||||||
@@ -301,23 +333,73 @@
|
|||||||
});
|
});
|
||||||
$('#add-petani-desa').select2();
|
$('#add-petani-desa').select2();
|
||||||
|
|
||||||
|
let croppieInstance = null;
|
||||||
|
|
||||||
$('#fotoPreview').on('click', function () {
|
$('#fotoPreview').on('click', function () {
|
||||||
|
$('#foto').val('');
|
||||||
$('#foto').click();
|
$('#foto').click();
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#foto').on('change', function (e) {
|
$('#foto').on('change', function (e) {
|
||||||
|
|
||||||
const file = e.target.files[0];
|
const file = e.target.files[0];
|
||||||
if (!file) return;
|
if (!file) return;
|
||||||
|
|
||||||
|
if(!file.type.match(/^image\/(png|jpeg|jpg)$/)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onload = function (event) {
|
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);
|
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
|
// 1 MB = 1048576 bytes
|
||||||
$.validator.addMethod('maxfilesize', function (value, element, param) {
|
$.validator.addMethod('maxfilesize', function (value, element, param) {
|
||||||
return this.optional(element) || (element.files[0] && element.files[0].size <= param);
|
return this.optional(element) || (element.files[0] && element.files[0].size <= param);
|
||||||
|
|||||||
@@ -1,5 +1,19 @@
|
|||||||
@extends('layout.main')
|
@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')
|
@section('content')
|
||||||
<div class="container-xxl flex-grow-1 container-p-y">
|
<div class="container-xxl flex-grow-1 container-p-y">
|
||||||
@if ($errors->any())
|
@if ($errors->any())
|
||||||
@@ -23,19 +37,23 @@
|
|||||||
<form id="editPetaniForm" action="{{ route('petani.update', $petani->id) }}" method="POST" enctype="multipart/form-data">
|
<form id="editPetaniForm" action="{{ route('petani.update', $petani->id) }}" method="POST" enctype="multipart/form-data">
|
||||||
@csrf
|
@csrf
|
||||||
@method('PUT')
|
@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>
|
<input type="file" id="foto" name="foto" accept="image/png,image/jpeg,image/jpg" hidden>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<div class="user-avatar-section">
|
<div class="user-avatar-section">
|
||||||
<div class=" d-flex align-items-center flex-column">
|
<div class=" d-flex align-items-center flex-column">
|
||||||
<img id="fotoPreview"
|
<img id="fotoPreview"
|
||||||
class="img-fluid rounded mb-4"
|
class="mb-3 shadow-sm rounded-3"
|
||||||
src="{{ $petani->foto ? route('petani.show-image', ['id' => $petani->id, 'field' => 'foto']) : asset('assets/img/avatars/1.png') }}"
|
src="{{ $petani->foto ? route('petani.show-image', ['id' => $petani->id, 'field' => 'foto']) : asset('assets/img/avatars/3.jpg') }}"
|
||||||
height="120" width="120"
|
|
||||||
alt="Foto profil petani"
|
alt="Foto profil petani"
|
||||||
data-bs-toggle="tooltip" data-bs-placement="top"
|
data-bs-toggle="tooltip" data-bs-placement="top"
|
||||||
data-bs-original-title="Klik untuk mengganti foto profil petani"
|
data-bs-original-title="Klik untuk mengganti foto profil petani"
|
||||||
style="cursor:pointer; object-fit:cover;" />
|
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">
|
<div class="user-info text-center">
|
||||||
<h5><i>{{ $petani->nama }}</i></h5>
|
<h5><i>{{ $petani->nama }}</i></h5>
|
||||||
<span class="badge bg-label-secondary">Petani {{ $petani->komoditas?->nama }}</span>
|
<span class="badge bg-label-secondary">Petani {{ $petani->komoditas?->nama }}</span>
|
||||||
@@ -234,6 +252,24 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</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
|
@endsection
|
||||||
|
|
||||||
@push('scripts')
|
@push('scripts')
|
||||||
@@ -306,24 +342,86 @@
|
|||||||
});
|
});
|
||||||
$('#edit-petani-desa').select2();
|
$('#edit-petani-desa').select2();
|
||||||
|
|
||||||
|
let croppieInstance = null;
|
||||||
|
|
||||||
$('#fotoPreview').on('click', function () {
|
$('#fotoPreview').on('click', function () {
|
||||||
|
$('#foto').val('');
|
||||||
$('#foto').click();
|
$('#foto').click();
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#foto').on('change', function (e) {
|
$('#foto').on('change', function (e) {
|
||||||
|
|
||||||
const file = e.target.files[0];
|
const file = e.target.files[0];
|
||||||
if (!file) return;
|
if (!file) return;
|
||||||
|
|
||||||
|
if(!file.type.match(/^image\/(png|jpeg|jpg)$/)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onload = function (event) {
|
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);
|
reader.readAsDataURL(file);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 1 MB = 1048576 bytes
|
// 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) {
|
$.validator.addMethod('maxfilesize', function (value, element, param) {
|
||||||
return this.optional(element) || (element.files[0] && element.files[0].size <= param);
|
return this.optional(element) || (element.files[0] && element.files[0].size <= param);
|
||||||
}, 'File size must be less than {0} bytes.');
|
}, '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="d-flex justify-content-start align-items-center mb-3">
|
||||||
<div class="avatar me-2">
|
<div class="avatar me-2">
|
||||||
@if (!isset($petani))
|
@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
|
@else
|
||||||
<img class="rounded-circle" alt="Foto Petani"
|
<img alt="Foto Petani"
|
||||||
src="{{ $petani->foto ? route('petani.show-image', ['id' => $petani->id, 'field' => 'foto']) : asset('assets/img/avatars/1.png') }}" />
|
src="{{ $petani->foto ? route('petani.show-image', ['id' => $petani->id, 'field' => 'foto']) : asset('assets/img/avatars/3.jpg') }}" class="rounded-3"/>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex flex-column">
|
<div class="d-flex flex-column">
|
||||||
|
|||||||
Reference in New Issue
Block a user