feat: avatar validation
This commit is contained in:
@@ -7,25 +7,64 @@
|
|||||||
@if($mode !== 'edit')
|
@if($mode !== 'edit')
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<label class="block text-[13px] font-bold text-gray-700 mb-1" for="avatar">Thay đổi ảnh đại diện</label>
|
<label class="block text-[13px] font-bold text-gray-700 mb-1" for="avatar">Thay đổi ảnh đại diện</label>
|
||||||
<input type="file" name="avatar" id="avatar" accept="image/*" class="block w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-3 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-[#3462f7] file:text-white hover:file:bg-[#254edb]" onchange="previewAvatar(event)">
|
<input type="file" name="avatar" id="avatar" accept="image/jpeg, image/png, image/webp" class="block w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-3 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-[#3462f7] file:text-white hover:file:bg-[#254edb]" onchange="previewAvatar(event)">
|
||||||
@error('avatar')<span class="text-red-500 text-[13px] font-semibold mt-1.5 block">{{ $message }}</span>@enderror
|
@error('avatar')<span class="text-red-500 text-[13px] font-semibold mt-1.5 block">{{ $message }}</span>@enderror
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if($mode !== 'edit')
|
@if($mode !== 'edit')
|
||||||
|
<x-error-dialog id="modal-avatar-size-error" title="Ảnh quá lớn" message="Ảnh đại diện không được vượt quá 2MB. Vui lòng chọn ảnh nhẹ hơn!" buttonText="Đã hiểu" />
|
||||||
|
|
||||||
@once
|
@once
|
||||||
@push('scripts')
|
@push('scripts')
|
||||||
<script>
|
<script>
|
||||||
function previewAvatar(event) {
|
// 1. Separate configuration to ease extension (Open/Closed Principle)
|
||||||
const input = event.target;
|
const AVATAR_CONFIG = {
|
||||||
if (input.files && input.files[0]) {
|
MAX_SIZE_BYTES: 2 * 1024 * 1024, // 2MB
|
||||||
|
DEFAULT_IMG_SRC: '{{ asset('images/avatar.png') }}',
|
||||||
|
PREVIEW_ELEMENT_ID: 'avatar-preview'
|
||||||
|
};
|
||||||
|
|
||||||
|
// 2. Separate Validation function (Single Responsibility Principle)
|
||||||
|
function isValidAvatar(file) {
|
||||||
|
if (file.size > AVATAR_CONFIG.MAX_SIZE_BYTES) {
|
||||||
|
if (window.openModal) {
|
||||||
|
window.openModal('modal-avatar-size-error');
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Separate UI Reset function (Single Responsibility Principle)
|
||||||
|
function resetAvatarInput(inputElement) {
|
||||||
|
inputElement.value = '';
|
||||||
|
document.getElementById(AVATAR_CONFIG.PREVIEW_ELEMENT_ID).src = AVATAR_CONFIG.DEFAULT_IMG_SRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Separate image rendering function (Single Responsibility Principle)
|
||||||
|
function renderAvatarPreview(file) {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onload = function(e) {
|
reader.onload = function(e) {
|
||||||
document.getElementById('avatar-preview').src = e.target.result;
|
document.getElementById(AVATAR_CONFIG.PREVIEW_ELEMENT_ID).src = e.target.result;
|
||||||
};
|
};
|
||||||
reader.readAsDataURL(input.files[0]);
|
reader.readAsDataURL(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 5. Main function acting as Controller
|
||||||
|
function previewAvatar(event) {
|
||||||
|
const input = event.target;
|
||||||
|
if (!input.files || !input.files[0]) return;
|
||||||
|
|
||||||
|
const file = input.files[0];
|
||||||
|
|
||||||
|
if (!isValidAvatar(file)) {
|
||||||
|
resetAvatarInput(input);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderAvatarPreview(file);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@endpush
|
@endpush
|
||||||
|
|||||||
Reference in New Issue
Block a user