-
+
-
+
-
+
-
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
+
+
+
+
+
+
+
+
+
-
+
-
+
-
+
-
![]()
+
![]()
-
-
+
-
+ 'cursor-not-allowed opacity-50': uploadLoading
+ }">
-
-
-
-
-
+
+
+
Format: JPG, JPEG, PNG (Max: 2MB per file, Max: 6 foto)
-
-
+
{{ uploadError }}
+
+
+ Batal
+
+ {{ loading ? 'Menyimpan...' : 'Tambah Item' }}
+
+
+ {{ loading ? 'Menyimpan...' : 'Simpan' }}
+
+
@@ -191,15 +144,17 @@ const uploadedImages = ref([]);
const isDragging = ref(false);
const uploadError = ref('');
const fileInput = ref(null);
-const userId = ref(1); // Sesuaikan dengan user yang login
+// TODO: Logika autentikasi user
+const userId = ref(1);
const isFormValid = computed(() => {
- return form.value.nama &&
- form.value.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.kategori &&
+ form.value.berat > 0 &&
+ form.value.kadar > 0 &&
+ form.value.harga_per_gram > 0 &&
+ form.value.harga_jual > 0 &&
+ uploadedImages.value.length > 0;
});
const calculateHargaJual = () => {
@@ -220,7 +175,6 @@ const loadExistingPhotos = async () => {
if (error.response?.status !== 404) {
console.error('Error loading existing photos:', error);
}
- // 404 is expected when no photos exist yet
}
};
@@ -238,64 +192,61 @@ 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 = '';
-
- // Validate file count
+
if (uploadedImages.value.length + files.length > 6) {
uploadError.value = 'Maksimal 6 foto yang dapat diupload';
return;
}
-
- // Validate file types and sizes
+
const validFiles = files.filter(file => {
const isValidType = ['image/jpeg', 'image/jpg', 'image/png'].includes(file.type);
- const isValidSize = file.size <= 2 * 1024 * 1024; // 2MB
-
+ 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;
}
-
+
return true;
});
-
+
if (validFiles.length === 0) return;
-
+
uploadLoading.value = true;
-
+
try {
for (const file of validFiles) {
const formData = new FormData();
formData.append('foto', file);
formData.append('id_user', userId.value);
-
+
const response = await axios.post('/api/foto/upload', formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
});
-
+
uploadedImages.value.push(response.data);
}
-
- // Clear file input
+
if (fileInput.value) {
fileInput.value.value = '';
}
-
+
} catch (error) {
console.error('Upload error:', error);
uploadError.value = error.response?.data?.message || 'Gagal mengupload foto';
@@ -315,21 +266,20 @@ const removeImage = async (imageId) => {
}
};
-const submitForm = async () => {
+const submitForm = async (addItem) => {
if (!isFormValid.value) {
alert('Mohon lengkapi semua field yang diperlukan');
return;
}
-
+
loading.value = true;
-
+
try {
const response = await axios.post('/api/produk', {
...form.value,
id_user: userId.value
});
-
- // Reset form
+
form.value = {
nama: '',
kategori: '',
@@ -338,19 +288,22 @@ const submitForm = async () => {
harga_per_gram: 0,
harga_jual: 0,
};
-
+
uploadedImages.value = [];
uploadError.value = '';
-
+
if (fileInput.value) {
fileInput.value.value = '';
}
-
- alert('Produk berhasil disimpan!');
-
+
+ if (addItem) {
+ alert('Produk berhasil ditambahkan. Silakan tambahkan produk lainnya.');
+ } else {
+ window.location.href = '/produk?message=Produk berhasil disimpan';
+ }
} catch (error) {
console.error('Submit error:', error);
-
+
if (error.response?.data?.errors) {
const errors = Object.values(error.response.data.errors).flat();
alert('Error: ' + errors.join(', '));
@@ -371,14 +324,12 @@ const resetPhotos = async () => {
}
};
-// Load existing photos on component mount
+const back = () => {
+ resetPhotos();
+ window.history.back();
+};
+
onMounted(() => {
loadExistingPhotos();
});
-
-// Clean up photos if user leaves without saving
-onUnmounted(() => {
- // Optional: You might want to clean up temporary photos here
- // resetPhotos();
-});
diff --git a/resources/js/pages/Produk.vue b/resources/js/pages/Produk.vue
index 566dce7..7104636 100644
--- a/resources/js/pages/Produk.vue
+++ b/resources/js/pages/Produk.vue
@@ -24,9 +24,9 @@
-
+
Tambah Produk
-
+
@@ -141,7 +141,7 @@ const currentFotoIndex = ref(0);
// Fetch data awal
onMounted(async () => {
try {
- const res = await axios.get("http://127.0.0.1:8000/api/produk");
+ const res = await axios.get("/api/produk");
products.value = res.data;
} catch (error) {
console.error("Gagal ambil data produk:", error);
@@ -172,7 +172,7 @@ const filteredProducts = computed(() => {
// buka overlay
async function openOverlay(id) {
try {
- const res = await axios.get(`http://127.0.0.1:8000/api/produk/${id}`);
+ const res = await axios.get(`/api/produk/${id}`);
detail.value = res.data;
currentFotoIndex.value = 0; // reset ke foto pertama
showOverlay.value = true;
diff --git a/routes/web.php b/routes/web.php
index fcdea34..ac3aef3 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -30,4 +30,4 @@ Route::prefix('api')->group(function () {
// Frontend SPA
Route::get('/{any}', function () {
return view('app');
-})->where('any', '.*');
+})->where('any', '^(?!storage|api).*$');
\ No newline at end of file