From fc71b974a6a109c786058a462db0d0c6bf858d03 Mon Sep 17 00:00:00 2001 From: adityaalfarison Date: Fri, 19 Sep 2025 16:21:17 +0700 Subject: [PATCH] update input produk using camera --- resources/js/pages/InputProduk.vue | 382 +++++++++++++---------------- 1 file changed, 177 insertions(+), 205 deletions(-) diff --git a/resources/js/pages/InputProduk.vue b/resources/js/pages/InputProduk.vue index 86dbbb5..e264117 100644 --- a/resources/js/pages/InputProduk.vue +++ b/resources/js/pages/InputProduk.vue @@ -1,7 +1,8 @@ @@ -129,55 +171,32 @@ import InputField from "../components/InputField.vue"; import InputSelect from "../components/InputSelect.vue"; import CreateItemModal from "../components/CreateItemModal.vue"; - const router = useRouter(); const form = ref({ - nama: '', - id_kategori: null, - berat: 0, - kadar: 0, - harga_per_gram: 0, - harga_jual: 0, + nama: '', id_kategori: null, berat: 0, kadar: 0, harga_per_gram: 0, harga_jual: 0, }); - const category = ref([]); - -const loadKategori = async () => { - try { - const response = await axios.get('/api/kategori', { - headers: { - Authorization: `Bearer ${localStorage.getItem("token")}`, - }, - }); - if (response.data && Array.isArray(response.data)) { - category.value = response.data.map(cat => ({ - value: cat.id, - label: cat.nama - })); - } - } catch (error) { - console.error('Error loading categories:', error); - } -}; - +const showUploadMenu = ref(false); +const fileInput = ref(null); const loading = ref(false); const uploadLoading = ref(false); const uploadedImages = ref([]); const isDragging = ref(false); const uploadError = ref(''); -const fileInput = ref(null); const errors = ref({}); const openItemModal = ref(false); const createdProduct = ref(null); +// Camera states +const showCamera = ref(false); +const video = ref(null); +const canvas = ref(null); +let stream = null; + const isFormValid = computed(() => { - return form.value.nama && - form.value.id_kategori && - form.value.berat > 0 && - form.value.kadar > 0 && - form.value.harga_per_gram > 0 && - form.value.harga_jual > 0 && + return form.value.nama && form.value.id_kategori && form.value.berat > 0 && + form.value.kadar > 0 && form.value.harga_per_gram > 0 && form.value.harga_jual > 0 && uploadedImages.value.length > 0; }); @@ -189,43 +208,42 @@ const calculateHargaJual = () => { } }; -const loadFoto = async () => { - loading.value = true; +const loadKategori = async () => { + try { + const response = await axios.get('/api/kategori', { + headers: { Authorization: `Bearer ${localStorage.getItem("token")}` }, + }); + if (response.data && Array.isArray(response.data)) { + category.value = response.data.map(cat => ({ value: cat.id, label: cat.nama })); + } + } catch (error) { + console.error('Error loading categories:', error); + } +}; +const loadFoto = async () => { try { const response = await axios.get(`/api/foto`, { - headers: { - Authorization: `Bearer ${localStorage.getItem("token")}`, - }, + headers: { Authorization: `Bearer ${localStorage.getItem("token")}` }, }); uploadedImages.value = response.data; - console.log(uploadedImages.value); } catch (e) { console.error(e); - uploadError.value = "Gagal memuat foto"; } - loading.value = false; -} - -const openCreateItemModal = (product) => { - createdProduct.value = product; - openItemModal.value = true; }; -const closeItemModal = () => { - openItemModal.value = false; - createdProduct.value = null; - resetForm(); - router.replace('/produk'); -}; - -const triggerFileInput = () => { +const toggleUploadMenu = () => { if (!uploadLoading.value && uploadedImages.value.length < 6) { - fileInput.value?.click(); + showUploadMenu.value = !showUploadMenu.value; } }; +const triggerFileUpload = () => { + showUploadMenu.value = false; + fileInput.value?.click(); +}; + const handleFileSelect = (event) => { const files = Array.from(event.target.files); uploadFiles(files); @@ -234,62 +252,36 @@ const handleFileSelect = (event) => { const handleDrop = (event) => { event.preventDefault(); isDragging.value = false; - if (uploadLoading.value || uploadedImages.value.length >= 6) return; - const files = Array.from(event.dataTransfer.files); uploadFiles(files); }; const uploadFiles = async (files) => { uploadError.value = ''; - if (uploadedImages.value.length + files.length > 6) { uploadError.value = 'Maksimal 6 foto yang dapat diupload'; return; } - const validFiles = files.filter(file => { const isValidType = ['image/jpeg', 'image/jpg', 'image/png'].includes(file.type); const isValidSize = file.size <= 2 * 1024 * 1024; - - if (!isValidType) { - uploadError.value = 'Format file harus JPG, JPEG, atau PNG'; - return false; - } - - if (!isValidSize) { - uploadError.value = 'Ukuran file maksimal 2MB'; - return false; - } - + if (!isValidType) { uploadError.value = 'Format file harus JPG, JPEG, atau PNG'; return false; } + if (!isValidSize) { uploadError.value = 'Ukuran file maksimal 2MB'; return false; } return true; }); - if (validFiles.length === 0) return; - uploadLoading.value = true; - try { for (const file of validFiles) { const formData = new FormData(); formData.append('foto', file); - const response = await axios.post('/api/foto', formData, { - headers: { - Authorization: `Bearer ${localStorage.getItem("token")}`, - 'Content-Type': 'multipart/form-data', - - }, + headers: { Authorization: `Bearer ${localStorage.getItem("token")}`, 'Content-Type': 'multipart/form-data' }, }); - uploadedImages.value.push(response.data); } - - if (fileInput.value) { - fileInput.value.value = ''; - } - + if (fileInput.value) fileInput.value.value = ''; } catch (error) { console.error('Upload error:', error); uploadError.value = error.response?.data?.message || 'Gagal mengupload foto'; @@ -299,16 +291,48 @@ const uploadFiles = async (files) => { }; const removeImage = async (id) => { - try { - await axios.delete(`/api/foto/${id}`, { - headers: { - Authorization: `Bearer ${localStorage.getItem("token")}`, - }, - }); - uploadedImages.value = uploadedImages.value.filter((i) => i.id !== id); - } catch { - uploadError.value = "Gagal menghapus foto"; - } + try { + await axios.delete(`/api/foto/${id}`, { + headers: { Authorization: `Bearer ${localStorage.getItem("token")}` }, + }); + uploadedImages.value = uploadedImages.value.filter((i) => i.id !== id); + } catch { + uploadError.value = "Gagal menghapus foto"; + } +}; + +// CAMERA FUNCTIONS +const openCameraModal = async () => { + showUploadMenu.value = false; + showCamera.value = true; + try { + stream = await navigator.mediaDevices.getUserMedia({ video: true }); + video.value.srcObject = stream; + } catch (err) { + console.error("Gagal akses kamera:", err); + alert("Tidak bisa mengakses kamera, cek izin browser!"); + closeCamera(); + } +}; + +const closeCamera = () => { + showCamera.value = false; + if (stream) { + stream.getTracks().forEach(track => track.stop()); + stream = null; + } +}; + +const capturePhoto = () => { + const ctx = canvas.value.getContext("2d"); + canvas.value.width = video.value.videoWidth; + canvas.value.height = video.value.videoHeight; + ctx.drawImage(video.value, 0, 0); + canvas.value.toBlob(async (blob) => { + if (!blob) return; + await uploadFiles([new File([blob], "camera_photo.png", { type: "image/png" })]); + closeCamera(); + }, "image/png"); }; const submitForm = async (addItem) => { @@ -316,89 +340,37 @@ const submitForm = async (addItem) => { alert('Mohon lengkapi semua field yang diperlukan'); return; } - loading.value = true; - try { - const response = await axios.post('/api/produk', form.value, - { - headers: { - Authorization: `Bearer ${localStorage.getItem("token")}`, - }, - } - ); - + const response = await axios.post('/api/produk', form.value, { + headers: { Authorization: `Bearer ${localStorage.getItem("token")}` }, + }); const createdProductData = response.data.data; - - // Reset form - form.value = { - nama: '', - id_kategori: '', - berat: 0, - kadar: 0, - harga_per_gram: 0, - harga_jual: 0, - }; - + form.value = { nama: '', id_kategori: '', berat: 0, kadar: 0, harga_per_gram: 0, harga_jual: 0 }; uploadedImages.value = []; uploadError.value = ''; - - if (fileInput.value) { - fileInput.value.value = ''; - } - + showUploadMenu.value = false; + if (fileInput.value) fileInput.value.value = ''; if (addItem) { openCreateItemModal(createdProductData); } else { window.location.href = '/produk?message=Produk berhasil disimpan'; } } catch (error) { - console.error('Submit error:', error); - - if (error.response?.status === 422 && error.response.data?.errors) { - // 🔥 simpan error validasi dari backend - errors.value = error.response.data.errors; - } else { - uploadError.value = error.response?.data?.message || 'Gagal menyimpan produk'; - } -} - finally { + console.error('Submit error:', error); + if (error.response?.status === 422) { + errors.value = error.response.data.errors || {}; + } else { + errors.value = { general: "Terjadi kesalahan saat menyimpan produk" }; + } + } finally { loading.value = false; } }; -const resetForm = async () => { - form.value = { - nama: '', - id_kategori: '', - berat: 0, - kadar: 0, - harga_per_gram: 0, - harga_jual: 0, - }; - try { - await axios.delete(`/api/foto/reset/${userId.value}`, { - headers: { - Authorization: `Bearer ${localStorage.getItem("token")}`, - }, - }); - uploadedImages.value = []; - } catch (error) { - console.error('Error resetting photos:', error); - } - uploadError.value = ''; - if (fileInput.value) { - fileInput.value.value = ''; - } -}; +const back = () => { router.push('/produk'); }; +const openCreateItemModal = (product) => { createdProduct.value = product; openItemModal.value = true; }; +const closeItemModal = () => { openItemModal.value = false; createdProduct.value = null; }; -const back = () => { - resetForm(); - window.history.back(); -}; - -onMounted(() => { - loadFoto(); - loadKategori(); -}); +onMounted(() => { loadFoto(); loadKategori(); });