|
1
|
-
+ |
-
+
-
+
- {{ item.produk?.nama || '' }}
+ {{ item.produk?.nama || '' }}
|
{{ item.produk.nama ? (item.nampan?.nama || 'Brankas') : '' }} |
@@ -240,6 +245,32 @@ const grandTotal = computed(() => {
return props.total + (ongkosBikin.value || 0)
})
+// Fungsi untuk menentukan style row berdasarkan jumlah item
+const getRowStyle = () => {
+ if (props.pesanan.length === 1) {
+ return { height: '126px' } // 2x lipat dari tinggi normal (48px)
+ }
+ return { height: '63px' } // Tinggi normal
+}
+
+
+
+// Fungsi untuk menentukan class gambar berdasarkan jumlah item
+const getImageClass = () => {
+ if (props.pesanan.length === 1) {
+ return 'w-25 h-25' // 2x lipat dari ukuran normal (w-10 h-10)
+ }
+ return 'w-12 h-12' // Ukuran normal
+}
+
+// Fungsi untuk menentukan class text berdasarkan jumlah item
+const getTextClass = () => {
+ if (props.pesanan.length === 1) {
+ return 'text-lg font-medium' // Text lebih besar untuk single item
+ }
+ return 'text-sm' // Text normal
+}
+
const getCurrentDate = () => {
const days = ['Minggu', 'Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat', 'Sabtu']
const months = ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12']
@@ -354,14 +385,6 @@ onMounted(() => {
}
})
-const pesananMinimal = computed(() => {
- const arr = [...props.pesanan]
- while (arr.length < 2) {
- arr.push({ produk: {}, harga_deal: 0, posisi: '' })
- }
- return arr
-})
-
function formatInput(e) {
let value = e.target.value.replace(/\D/g, "");
ongkosBikin.value = value ? parseInt(value, 10) : null;
diff --git a/resources/js/components/StrukView.vue b/resources/js/components/StrukView.vue
index 34e8f81..0134c61 100644
--- a/resources/js/components/StrukView.vue
+++ b/resources/js/components/StrukView.vue
@@ -1,7 +1,9 @@
-
+
+
+
@@ -66,23 +68,23 @@
-
+
|
1
|
-
+ |
-
+
-
+
IMG
-
+
- {{ item.produk?.nama || '' }}
+ {{ item.produk?.nama || '' }}
|
{{ item.posisi_asal || 'Brankas' }}
@@ -109,8 +111,7 @@
PERHATIAN
- Berat barang telah ditimbang dan disaksikan oleh pembeli.
- - Barang yang dikembalikan menurut harga pasaran dan dipotong ongkos bikin, barang rusak
- lain harga.
+ - Barang yang dikembalikan menurut harga pasaran dan dipotong ongkos bikin, barang rusak lain harga.
- Barang yang sudah dibeli berarti sudah diperiksa dan disetujui.
- Surat ini harap dibawa pada saat menjual kembali.
@@ -133,7 +134,7 @@
Ongkos bikin
diluar harga jual
-
+
Rp
{{ (transaksi.ongkos_bikin || 0).toLocaleString() }},-
@@ -154,7 +155,7 @@
-
+
@@ -196,50 +197,53 @@ const props = defineProps({
const emit = defineEmits(['close'])
-// Format tanggal sesuai dengan format yang ada
const formatDate = (dateString) => {
if (!dateString) return '-'
-
- const days = ['Minggu', 'Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat', 'Sabtu']
- const months = ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12']
-
+ const days = ['Minggu','Senin','Selasa','Rabu','Kamis','Jumat','Sabtu']
+ const months = ['01','02','03','04','05','06','07','08','09','10','11','12']
const date = new Date(dateString)
const dayName = days[date.getDay()]
const day = String(date.getDate()).padStart(2, '0')
const month = months[date.getMonth()]
const year = date.getFullYear()
-
return `${dayName}/${day}-${month}-${year}`
}
-// Menambahkan minimal 2 baris untuk tampilan yang konsisten
const itemsWithMinimal = computed(() => {
- console.log('Transaksi data in StrukView:', props.transaksi)
-
- // Coba berbagai kemungkinan nama field
const items = props.transaksi.itemTransaksi ||
props.transaksi.items ||
props.transaksi.item_transaksi ||
[]
-
- console.log('Items found:', items)
-
const arr = [...items]
-
- // Pastikan minimal ada 2 baris untuk tampilan yang rapi
- while (arr.length < 2) {
- arr.push({ produk: {}, harga_deal: 0, posisi_asal: '' })
- }
-
+ // biar minimal ada 1 row terlihat
+ if (arr.length === 0) arr.push({ produk: {}, harga_deal: 0, posisi_asal: '' })
return arr
})
-// Fungsi print (bisa dikembangkan lebih lanjut)
+// === Tambahan style/helper sama dengan strukoverlay ===
+const getRowStyle = () => {
+ if (itemsWithMinimal.value.length === 1) {
+ return { height: '126px' }
+ }
+ return { height: '63px' }
+}
+const getImageClass = () => {
+ if (itemsWithMinimal.value.length === 1) {
+ return 'w-25 h-25'
+ }
+ return 'w-12 h-12'
+}
+const getTextClass = () => {
+ if (itemsWithMinimal.value.length === 1) {
+ return 'text-lg font-medium'
+ }
+ return 'text-sm'
+}
+
const handlePrint = () => {
window.print()
}
-// Format number helper
const formatNumber = (number) => {
if (!number) return 0
return parseFloat(number).toLocaleString('id-ID')
@@ -256,15 +260,20 @@ const formatNumber = (number) => {
/* Print styles */
@media print {
- .fixed {
- position: static !important;
+ body * {
+ visibility: hidden;
}
-
- .bg-black\/75 {
- background: none !important;
+ .print-area, .print-area * {
+ visibility: visible;
}
-
- button {
+ .print-area {
+ position: absolute;
+ top: 0;
+ left: 0;
+ transform: scale(0.5);
+ transform-origin: top left;
+ }
+ .no-print {
display: none !important;
}
}
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 @@
-
+
+
Produk Baru
@@ -10,10 +11,8 @@
-
-
- {{ errors.nama[0] }}
-
+
+ {{ errors.nama[0] }}
@@ -25,7 +24,7 @@
+ @input="calculateHargaJual" />
@@ -36,12 +35,13 @@
-
+
-
+
@@ -50,55 +50,82 @@
-
-
+
![]()
-
-
-
-
-
-
+
+
Format: JPG, JPEG, PNG (Max: 2MB per file, Max: 6 foto)
-
{{ uploadError }}
@@ -108,15 +135,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -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(); });
| |