[Update] Minor bug fixed

This commit is contained in:
Baghaztra 2025-09-17 15:37:29 +07:00
parent 8674ec9828
commit 70c15edc27
4 changed files with 149 additions and 70 deletions

View File

@ -46,20 +46,20 @@
@click="openMovePopup(item)">
<!-- Gambar & Info Produk -->
<div class="flex items-center gap-3">
<img v-if="item.produk.foto?.length" :src="item.produk.foto[0].url"
<img v-if="item.produk?.foto?.length" :src="item.produk?.foto[0].url"
class="size-12 object-cover rounded"
@error="handleImageError" />
<div class="size-12 bg-gray-200 rounded flex items-center justify-center" v-else>
<i class="fas fa-image text-gray-400"></i>
</div>
<div>
<p class="font-semibold text-D">{{ item.produk.nama }}</p>
<p class="font-semibold text-D">{{ item.produk?.nama }}</p>
<p class="text-sm text-gray-500 font-semibold">{{ item.kode_item }}</p>
</div>
</div>
<!-- Berat -->
<span class="font-medium text-D">{{ item.produk.berat }}g</span>
<span class="font-medium text-D">{{ item.produk?.berat }}g</span>
</div>
</div>
@ -191,7 +191,9 @@ const qrCodeUrl = computed(() => {
// Computed untuk statistik
const totalWeight = computed(() => {
const total = filteredItems.value.reduce((sum, item) => sum + (item.produk.berat || 0), 0);
const total = filteredItems.value.reduce((sum, item) => {
return sum + (item?.produk?.berat || 0);
}, 0);
return total.toFixed(2);
});
@ -332,7 +334,6 @@ const refreshData = async () => {
// Filter hanya item yang ada di brankas (id_nampan = null atau tidak ada)
items.value = itemRes.data.filter(item => !item.id_nampan);
trays.value = trayRes.data;
} catch (err) {
console.error("Error fetching data:", err);
alert.value = { error: err.response?.data?.message || "Gagal mengambil data" };

View File

@ -31,7 +31,14 @@
<!-- Input Harga Jual -->
<div>
<label class="block text-sm font-medium text-D">Harga Jual</label>
<InputField v-model="hargaJual" type="number" placeholder="Masukkan Harga Jual" />
<input
type="text"
v-model="hargaJualFormatted"
@input="formatHargaInput"
@keypress="onlyNumbers"
placeholder="Masukkan Harga Jual"
class="bg-A focus:outline-none focus:border-C focus:ring focus:ring-D focus:ring-opacity-50 p-2 w-full rounded-md border border-B shadow-sm sm:text-sm"
/>
</div>
<!-- Tombol Aksi -->
@ -120,6 +127,7 @@ const kodeItem = ref("");
const info = ref("");
const error = ref("");
const hargaJual = ref(null);
const hargaJualFormatted = ref("");
const item = ref(null);
const loadingItem = ref(false);
const pesanan = ref([]);
@ -131,6 +139,45 @@ const showStruk = ref(false);
let errorTimeout = null;
let infoTimeout = null;
// Format angka dengan pemisah ribuan
const formatNumber = (num) => {
if (!num) return "";
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ".");
};
// Menghapus format dan mengambil angka asli
const unformatNumber = (str) => {
if (!str) return null;
const cleaned = str.replace(/\./g, "");
const number = parseInt(cleaned);
return isNaN(number) ? null : number;
};
// Handler untuk format input harga
const formatHargaInput = (event) => {
const value = event.target.value;
// Hapus semua karakter selain angka
const cleanValue = value.replace(/\D/g, "");
if (cleanValue) {
// Format dengan pemisah ribuan
const formatted = formatNumber(cleanValue);
hargaJualFormatted.value = formatted;
hargaJual.value = parseInt(cleanValue);
} else {
hargaJualFormatted.value = "";
hargaJual.value = null;
}
};
// Hanya izinkan angka saat mengetik
const onlyNumbers = (event) => {
const char = String.fromCharCode(event.which);
if (!/[0-9]/.test(char)) {
event.preventDefault();
}
};
const inputItem = async () => {
if (!kodeItem.value) return;
@ -149,6 +196,8 @@ const inputItem = async () => {
});
item.value = response.data;
hargaJual.value = item.value.produk.harga_jual;
// Format harga untuk tampilan
hargaJualFormatted.value = formatNumber(item.value.produk.harga_jual);
console.log(item.value);
@ -167,6 +216,7 @@ const inputItem = async () => {
error.value = "Item tidak ditemukan";
info.value = "";
hargaJual.value = null;
hargaJualFormatted.value = "";
item.value = null;
errorTimeout = setTimeout(() => {
@ -201,6 +251,7 @@ const tambahItem = () => {
// Reset input fields
kodeItem.value = "";
hargaJual.value = null;
hargaJualFormatted.value = "";
item.value = null;
info.value = "";
clearTimeout(infoTimeout);

View File

@ -19,37 +19,27 @@
<i class="fab fa-whatsapp text-green-500 text-xl"></i> 08158851178
</p>
<p class=" text-sm">{{ generateTransactionCode() }}</p>
</div>
<div class="absolute inset-x-0 top-[-48px] flex flex-col items-center">
<img :src="logo" alt="Logo" class="h-40" />
</div>
<div class="grid grid-cols-[130px_1fr] gap-y-0 text-xs items-center -mt-5 relative z-10">
<div class="text-right font-semibold pr-3">Tanggal :</div>
<p class="mt-1 text-left pl-2">{{ getCurrentDate() }}</p>
<div class="text-right font-semibold pr-3">Nama :</div>
<inputField v-model="namaPembeli" class="h-7 text-sm rounded bg-blue-200 w-full" />
<div class="text-right font-semibold pr-3">Alamat :</div>
<inputField v-model="alamat" class="h-7 px-2 text-sm rounded bg-blue-200 w-full" />
<div class="text-right font-semibold pr-3">No.Hp :</div>
<inputField v-model="nomorTelepon" class="h-7 px-2 text-sm rounded bg-blue-200 w-full" />
</div>
</div>
<div class="flex justify-between items-center mb-1 w-full">
<div class="flex gap-2">
<img :src="logo_bca" alt="Logo_bca" class="h-5" />
@ -88,7 +78,7 @@
</template>
{{ item.produk?.nama || '' }}
</td>
<td class="border-r border-D">{{ item.nampan?.nama || 'Brankas' }}</td>
<td class="border-r border-D">{{ item.produk.nama ? (item.nampan?.nama || 'Brankas') : '' }}</td>
<td class="border-r border-D">
<span v-if="item.produk?.berat">{{ item.produk.berat }}g</span>
</td>
@ -133,14 +123,9 @@
</div>
<div class="flex items-center w-40">
<p>Rp</p>
<input
type="text"
v-model="ongkosBikinFormatted"
@input="formatInput"
class="h-7 px-2 text-sm rounded bg-blue-200 text-left w-full"
/>
<input type="text" v-model="ongkosBikinFormatted" @input="formatInput"
class="h-7 px-2 text-sm rounded bg-blue-200 text-left w-full" />
</div>
</div>
<!-- Total -->
@ -170,7 +155,30 @@
<p class="absolute p-8 bottom-0 left-0 w-full text-left text-xs bg-D text-white py-1">
Terima kasih sudah berbelanja dengan kami
</p>
</div>
</div>
<!-- Simple Toast Alert -->
<div v-if="showToast"
class="fixed top-4 left-1/2 transform -translate-x-1/2 z-[10001]
transition-all duration-300 ease-in-out"
:class="toastClasses">
<div class="flex items-center gap-2 px-4 py-3 rounded-lg shadow-lg max-w-sm">
<!-- Icon -->
<div class="flex-shrink-0">
<svg v-if="toastType === 'error'" class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd" />
</svg>
<svg v-else-if="toastType === 'success'" class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd" />
</svg>
<svg v-else class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd" />
</svg>
</div>
<!-- Message -->
<p class="text-sm font-medium">{{ toastMessage }}</p>
</div>
</div>
</template>
@ -204,7 +212,6 @@ const props = defineProps({
}
})
const emit = defineEmits(['close', 'confirm'])
const namaPembeli = ref('')
@ -213,7 +220,22 @@ const alamat = ref('')
const ongkosBikin = ref(0)
const selectedSales = ref(null)
const salesOptions = ref([])
const ongkosBikinFormatted = ref("");
const ongkosBikinFormatted = ref("")
// Simple Toast State
const showToast = ref(false)
const toastType = ref('error') // 'error', 'success', 'info'
const toastMessage = ref('')
const toastClasses = computed(() => {
const baseClasses = 'text-white'
const typeClasses = {
error: 'bg-red-500',
success: 'bg-green-500',
info: 'bg-blue-500'
}
return `${baseClasses} ${typeClasses[toastType.value]}`
})
const grandTotal = computed(() => {
return props.total + (ongkosBikin.value || 0)
@ -238,6 +260,17 @@ const generateTransactionCode = () => {
return `TRS-${timestamp}`
}
// Simple Toast Function
const showSimpleToast = (type, message, duration = 3000) => {
toastType.value = type
toastMessage.value = message
showToast.value = true
setTimeout(() => {
showToast.value = false
}, duration)
}
const fetchSales = async () => {
try {
const response = await axios.get('/api/sales', {
@ -251,7 +284,6 @@ const fetchSales = async () => {
label: sales.nama
}))
if (salesOptions.value.length > 0) {
selectedSales.value = salesOptions.value[0].value
}
@ -261,28 +293,26 @@ const fetchSales = async () => {
}
const handleSimpan = () => {
if (!namaPembeli.value.trim()) {
alert('Nama pembeli harus diisi!')
showSimpleToast('error', 'Nama pembeli harus diisi!')
return
}
if (!nomorTelepon.value.trim()) {
alert('Nomor telepon harus diisi!')
showSimpleToast('error', 'Nomor telepon harus diisi!')
return
}
if (!alamat.value.trim()) {
alert('Alamat harus diisi!')
showSimpleToast('error', 'Alamat harus diisi!')
return
}
if (!selectedSales.value) {
alert('Sales harus dipilih!')
showSimpleToast('error', 'Sales harus dipilih!')
return
}
simpanTransaksi({
id_sales: selectedSales.value,
nama_pembeli: namaPembeli.value,
@ -294,7 +324,6 @@ const handleSimpan = () => {
})
}
const simpanTransaksi = async (dataTransaksi) => {
console.log('Data transaksi yang akan disimpan:', dataTransaksi);
@ -305,15 +334,19 @@ const simpanTransaksi = async (dataTransaksi) => {
},
});
showSimpleToast('success', 'Transaksi berhasil disimpan!', 2000)
// Delay untuk memberikan waktu user membaca notifikasi
setTimeout(() => {
props.pesanan.value = [];
props.isOpen = false;
window.location.reload();
}, 2200);
} catch (error) {
console.error('Error saving transaksi:', error);
alert('Error menyimpan transaksi: ' + (error.response?.data?.message || error.message));
const errorMessage = error.response?.data?.message || error.message || 'Terjadi kesalahan saat menyimpan transaksi';
showSimpleToast('error', `Error: ${errorMessage}`, 4000);
}
};
@ -332,18 +365,12 @@ const pesananMinimal = computed(() => {
})
function formatInput(e) {
let value = e.target.value.replace(/\D/g, "");
ongkosBikin.value = value ? parseInt(value, 10) : null;
ongkosBikinFormatted.value = value
? new Intl.NumberFormat("id-ID").format(value)
: "";
}
</script>
<style scoped>

View File

@ -61,18 +61,18 @@
>
<div class="flex items-center gap-3">
<img
v-if="item.produk.foto && item.produk.foto.length > 0"
:src="item.produk.foto[0].url"
v-if="item.produk?.foto && item.produk?.foto.length > 0"
:src="item.produk?.foto[0].url"
alt="foto produk"
class="size-12 object-cover rounded"
/>
<div class="text-D">
<p class="text-sm">{{ item.produk.nama }}</p>
<p class="text-sm">{{ item.produk?.nama }}</p>
<p class="text-sm font-medium">{{ item.kode_item }}</p>
</div>
</div>
<div class="flex items-center gap-2">
<span class="font-medium">{{ item.produk.berat }}g</span>
<span class="font-medium">{{ item.produk?.berat }}g</span>
</div>
</div>
</div>
@ -107,7 +107,7 @@
{{ selectedItem.kode_item }}
</div>
<div class="text-center text-gray-700 font-medium mb-3">
{{ selectedItem.produk.nama }}
{{ selectedItem.produk?.nama }}
</div>
<div class="flex justify-center mb-4">
@ -248,7 +248,7 @@ const saveMove = async () => {
// Hitung total berat
const totalWeight = (tray) => {
if (!tray.items) return 0;
const total = tray.items.reduce((sum, item) => sum + (item.produk.berat || 0), 0);
const total = tray.items.reduce((sum, item) => sum + (item.produk?.berat || 0), 0);
return total.toFixed(2);
};