Merge branch 'development' into production
This commit is contained in:
commit
06ec582ffb
@ -75,6 +75,7 @@ class TransaksiController extends Controller
|
|||||||
'kasir',
|
'kasir',
|
||||||
'sales',
|
'sales',
|
||||||
'itemTransaksi.produk',
|
'itemTransaksi.produk',
|
||||||
|
'itemTransaksi.produk.foto',
|
||||||
'itemTransaksi' => function ($query) {
|
'itemTransaksi' => function ($query) {
|
||||||
$query->orderBy('created_at', 'asc');
|
$query->orderBy('created_at', 'asc');
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
driver/L120_x64_213UsHomeExportAsiaML.exe
Normal file
BIN
driver/L120_x64_213UsHomeExportAsiaML.exe
Normal file
Binary file not shown.
@ -2,9 +2,23 @@
|
|||||||
<ConfirmDeleteModal v-if="showDeleteModal" :isOpen="showDeleteModal" title="Konfirmasi"
|
<ConfirmDeleteModal v-if="showDeleteModal" :isOpen="showDeleteModal" title="Konfirmasi"
|
||||||
message="Yakin ingin menghapus item ini?" @confirm="hapusPesanan" @cancel="closeDeleteModal" />
|
message="Yakin ingin menghapus item ini?" @confirm="hapusPesanan" @cancel="closeDeleteModal" />
|
||||||
|
|
||||||
<!-- ==== TAMBAHAN: Struk Overlay ==== -->
|
<!-- Struk Input Overlay -->
|
||||||
<StrukOverlay v-if="showStruk" :isOpen="showStruk" :pesanan="pesanan" :total="total" @close="closeStruk" />
|
<StrukOverlay
|
||||||
<!-- ==== END TAMBAHAN ==== -->
|
v-if="showStruk"
|
||||||
|
:isOpen="showStruk"
|
||||||
|
:pesanan="pesanan"
|
||||||
|
:total="total"
|
||||||
|
@close="closeStruk"
|
||||||
|
@transaksi-saved="handleTransaksiSaved"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- Struk View (Print) Overlay -->
|
||||||
|
<StrukView
|
||||||
|
v-if="showStrukView"
|
||||||
|
:isOpen="showStrukView"
|
||||||
|
:transaksi="savedTransaksi"
|
||||||
|
@close="closeStrukView"
|
||||||
|
/>
|
||||||
|
|
||||||
<div class="p-2 sm:p-4">
|
<div class="p-2 sm:p-4">
|
||||||
<!-- Grid Form & Total -->
|
<!-- Grid Form & Total -->
|
||||||
@ -115,13 +129,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed } from "vue";
|
import { ref, computed } from "vue";
|
||||||
import InputField from "./InputField.vue";
|
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import ConfirmDeleteModal from "./ConfirmDeleteModal.vue";
|
import ConfirmDeleteModal from "./ConfirmDeleteModal.vue";
|
||||||
import StrukOverlay from "./StrukOverlay.vue";
|
import StrukOverlay from "./StrukOverlay.vue";
|
||||||
|
import StrukView from "./StrukView.vue";
|
||||||
|
|
||||||
|
// Emit untuk komunikasi dengan parent
|
||||||
|
const emit = defineEmits(['transaksi-saved']);
|
||||||
|
|
||||||
const kodeItem = ref("");
|
const kodeItem = ref("");
|
||||||
const info = ref("");
|
const info = ref("");
|
||||||
@ -131,21 +147,21 @@ const hargaJualFormatted = ref("");
|
|||||||
const item = ref(null);
|
const item = ref(null);
|
||||||
const loadingItem = ref(false);
|
const loadingItem = ref(false);
|
||||||
const pesanan = ref([]);
|
const pesanan = ref([]);
|
||||||
const showDeleteModal = ref(false)
|
const showDeleteModal = ref(false);
|
||||||
const deleteIndex = ref(null)
|
const deleteIndex = ref(null);
|
||||||
|
|
||||||
const showStruk = ref(false);
|
const showStruk = ref(false);
|
||||||
|
const showStrukView = ref(false);
|
||||||
|
const savedTransaksi = ref(null);
|
||||||
|
|
||||||
let errorTimeout = null;
|
let errorTimeout = null;
|
||||||
let infoTimeout = null;
|
let infoTimeout = null;
|
||||||
|
|
||||||
// Format angka dengan pemisah ribuan
|
|
||||||
const formatNumber = (num) => {
|
const formatNumber = (num) => {
|
||||||
if (!num) return "";
|
if (!num) return "";
|
||||||
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ".");
|
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ".");
|
||||||
};
|
};
|
||||||
|
|
||||||
// Menghapus format dan mengambil angka asli
|
|
||||||
const unformatNumber = (str) => {
|
const unformatNumber = (str) => {
|
||||||
if (!str) return null;
|
if (!str) return null;
|
||||||
const cleaned = str.replace(/\./g, "");
|
const cleaned = str.replace(/\./g, "");
|
||||||
@ -153,14 +169,11 @@ const unformatNumber = (str) => {
|
|||||||
return isNaN(number) ? null : number;
|
return isNaN(number) ? null : number;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handler untuk format input harga
|
|
||||||
const formatHargaInput = (event) => {
|
const formatHargaInput = (event) => {
|
||||||
const value = event.target.value;
|
const value = event.target.value;
|
||||||
// Hapus semua karakter selain angka
|
|
||||||
const cleanValue = value.replace(/\D/g, "");
|
const cleanValue = value.replace(/\D/g, "");
|
||||||
|
|
||||||
if (cleanValue) {
|
if (cleanValue) {
|
||||||
// Format dengan pemisah ribuan
|
|
||||||
const formatted = formatNumber(cleanValue);
|
const formatted = formatNumber(cleanValue);
|
||||||
hargaJualFormatted.value = formatted;
|
hargaJualFormatted.value = formatted;
|
||||||
hargaJual.value = parseInt(cleanValue);
|
hargaJual.value = parseInt(cleanValue);
|
||||||
@ -170,7 +183,6 @@ const formatHargaInput = (event) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Hanya izinkan angka saat mengetik
|
|
||||||
const onlyNumbers = (event) => {
|
const onlyNumbers = (event) => {
|
||||||
const char = String.fromCharCode(event.which);
|
const char = String.fromCharCode(event.which);
|
||||||
if (!/[0-9]/.test(char)) {
|
if (!/[0-9]/.test(char)) {
|
||||||
@ -196,11 +208,8 @@ const inputItem = async () => {
|
|||||||
});
|
});
|
||||||
item.value = response.data;
|
item.value = response.data;
|
||||||
hargaJual.value = item.value.produk.harga_jual;
|
hargaJual.value = item.value.produk.harga_jual;
|
||||||
// Format harga untuk tampilan
|
|
||||||
hargaJualFormatted.value = formatNumber(item.value.produk.harga_jual);
|
hargaJualFormatted.value = formatNumber(item.value.produk.harga_jual);
|
||||||
|
|
||||||
// console.log(item.value);
|
|
||||||
|
|
||||||
if (item.value.is_sold) {
|
if (item.value.is_sold) {
|
||||||
throw new Error("Item sudah terjual");
|
throw new Error("Item sudah terjual");
|
||||||
}
|
}
|
||||||
@ -231,8 +240,7 @@ const tambahItem = () => {
|
|||||||
if (!item.value || !hargaJual.value) {
|
if (!item.value || !hargaJual.value) {
|
||||||
error.value = "Scan atau masukkan kode item untuk dijual.";
|
error.value = "Scan atau masukkan kode item untuk dijual.";
|
||||||
if (kodeItem.value) {
|
if (kodeItem.value) {
|
||||||
error.value =
|
error.value = "Masukkan harga jual, atau input dari kode item lagi.";
|
||||||
"Masukkan harga jual, atau input dari kode item lagi.";
|
|
||||||
}
|
}
|
||||||
clearTimeout(errorTimeout);
|
clearTimeout(errorTimeout);
|
||||||
errorTimeout = setTimeout(() => {
|
errorTimeout = setTimeout(() => {
|
||||||
@ -241,14 +249,12 @@ const tambahItem = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// harga deal
|
|
||||||
item.value.kode_item = Number(kodeItem.value);
|
item.value.kode_item = Number(kodeItem.value);
|
||||||
item.value.harga_deal = Number(hargaJual.value);
|
item.value.harga_deal = Number(hargaJual.value);
|
||||||
item.value.posisi = item.value.nampan ? item.value.nampan.nama : "Brankas";
|
item.value.posisi = item.value.nampan ? item.value.nampan.nama : "Brankas";
|
||||||
|
|
||||||
pesanan.value.push(item.value);
|
pesanan.value.push(item.value);
|
||||||
|
|
||||||
// Reset input fields
|
|
||||||
kodeItem.value = "";
|
kodeItem.value = "";
|
||||||
hargaJual.value = null;
|
hargaJual.value = null;
|
||||||
hargaJualFormatted.value = "";
|
hargaJualFormatted.value = "";
|
||||||
@ -258,23 +264,22 @@ const tambahItem = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const openDeleteModal = (index) => {
|
const openDeleteModal = (index) => {
|
||||||
deleteIndex.value = index
|
deleteIndex.value = index;
|
||||||
showDeleteModal.value = true
|
showDeleteModal.value = true;
|
||||||
}
|
};
|
||||||
|
|
||||||
const closeDeleteModal = () => {
|
const closeDeleteModal = () => {
|
||||||
showDeleteModal.value = false
|
showDeleteModal.value = false;
|
||||||
deleteIndex.value = null
|
deleteIndex.value = null;
|
||||||
}
|
};
|
||||||
|
|
||||||
const hapusPesanan = () => {
|
const hapusPesanan = () => {
|
||||||
if (deleteIndex.value !== null) {
|
if (deleteIndex.value !== null) {
|
||||||
pesanan.value.splice(deleteIndex.value, 1)
|
pesanan.value.splice(deleteIndex.value, 1);
|
||||||
}
|
}
|
||||||
closeDeleteModal()
|
closeDeleteModal();
|
||||||
}
|
};
|
||||||
|
|
||||||
// ==== MODIFIKASI: konfirmasiPenjualan sekarang menampilkan struk ====
|
|
||||||
const konfirmasiPenjualan = () => {
|
const konfirmasiPenjualan = () => {
|
||||||
if (pesanan.value.length === 0) {
|
if (pesanan.value.length === 0) {
|
||||||
error.value = "Belum ada item yang dipesan.";
|
error.value = "Belum ada item yang dipesan.";
|
||||||
@ -284,17 +289,37 @@ const konfirmasiPenjualan = () => {
|
|||||||
}, 5000);
|
}, 5000);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log(pesanan.value)
|
|
||||||
// Tampilkan struk overlay
|
|
||||||
showStruk.value = true;
|
showStruk.value = true;
|
||||||
};
|
};
|
||||||
// ==== END MODIFIKASI ====
|
|
||||||
|
|
||||||
// ==== TAMBAHAN: Fungsi untuk menutup struk ====
|
|
||||||
const closeStruk = () => {
|
const closeStruk = () => {
|
||||||
showStruk.value = false;
|
showStruk.value = false;
|
||||||
};
|
};
|
||||||
// ==== END TAMBAHAN ====
|
|
||||||
|
const closeStrukView = () => {
|
||||||
|
showStrukView.value = false;
|
||||||
|
savedTransaksi.value = null;
|
||||||
|
|
||||||
|
// Reset pesanan setelah menutup struk view
|
||||||
|
pesanan.value = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handler ketika transaksi berhasil disimpan
|
||||||
|
const handleTransaksiSaved = (transaksiData) => {
|
||||||
|
// Tutup StrukOverlay
|
||||||
|
showStruk.value = false;
|
||||||
|
|
||||||
|
// Simpan data transaksi
|
||||||
|
savedTransaksi.value = transaksiData;
|
||||||
|
|
||||||
|
// Emit ke parent (Kasir.vue)
|
||||||
|
emit('transaksi-saved', transaksiData);
|
||||||
|
|
||||||
|
// Buka StrukView untuk print
|
||||||
|
setTimeout(() => {
|
||||||
|
showStrukView.value = true;
|
||||||
|
}, 300);
|
||||||
|
};
|
||||||
|
|
||||||
const total = computed(() => {
|
const total = computed(() => {
|
||||||
let sum = 0;
|
let sum = 0;
|
||||||
|
|||||||
@ -64,7 +64,6 @@
|
|||||||
</thead>
|
</thead>
|
||||||
|
|
||||||
<tbody>
|
<tbody>
|
||||||
<!-- Item rows dengan dynamic height -->
|
|
||||||
<tr v-for="(item, index) in props.pesanan" :key="index"
|
<tr v-for="(item, index) in props.pesanan" :key="index"
|
||||||
class="text-center"
|
class="text-center"
|
||||||
:style="getRowStyle()">
|
:style="getRowStyle()">
|
||||||
@ -95,9 +94,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<!-- Bagian bawah -->
|
|
||||||
<div class="flex text-sm mt-2">
|
<div class="flex text-sm mt-2">
|
||||||
<!-- PERHATIAN -->
|
|
||||||
<div class="w-[40%] p-2 text-left">
|
<div class="w-[40%] p-2 text-left">
|
||||||
<p class="font-semibold">PERHATIAN</p>
|
<p class="font-semibold">PERHATIAN</p>
|
||||||
<ol class="list-decimal ml-4 text-xs space-y-1">
|
<ol class="list-decimal ml-4 text-xs space-y-1">
|
||||||
@ -109,17 +106,14 @@
|
|||||||
</ol>
|
</ol>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- SALES -->
|
|
||||||
<div class="w-[20%] p-2 flex flex-col items-center justify-center">
|
<div class="w-[20%] p-2 flex flex-col items-center justify-center">
|
||||||
<p><strong>Hormat Kami</strong></p>
|
<p><strong>Hormat Kami</strong></p>
|
||||||
<inputSelect v-model="selectedSales" :options="salesOptions"
|
<inputSelect v-model="selectedSales" :options="salesOptions"
|
||||||
class="mt-16 text-sm rounded bg-B cursor-pointer !w-[160px] text-center [option]:text-left" />
|
class="mt-16 text-sm rounded bg-B cursor-pointer !w-[160px] text-center [option]:text-left" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ONGKOS & TOTAL -->
|
|
||||||
<div class="ml-auto w-[25%] p-2 flex flex-col justify-between">
|
<div class="ml-auto w-[25%] p-2 flex flex-col justify-between">
|
||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
<!-- Ongkos bikin -->
|
|
||||||
<div class="flex items-start justify-between ">
|
<div class="flex items-start justify-between ">
|
||||||
<div class="flex flex-col ">
|
<div class="flex flex-col ">
|
||||||
<p class="font-semibold">Ongkos bikin</p>
|
<p class="font-semibold">Ongkos bikin</p>
|
||||||
@ -132,7 +126,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Total -->
|
|
||||||
<div class="flex items-center justify-between -mt-4">
|
<div class="flex items-center justify-between -mt-4">
|
||||||
<p class="font-semibold">Total Harga</p>
|
<p class="font-semibold">Total Harga</p>
|
||||||
<div class="flex items-center w-40">
|
<div class="flex items-center w-40">
|
||||||
@ -144,13 +137,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Tombol -->
|
|
||||||
<div class="flex justify-end gap-2 mt-4">
|
<div class="flex justify-end gap-2 mt-4">
|
||||||
<button @click="$emit('close')" class="bg-gray-400 text-white px-6 py-2 rounded">
|
<button @click="$emit('close')" class="bg-gray-400 text-white px-6 py-2 rounded">
|
||||||
Batal
|
Batal
|
||||||
</button>
|
</button>
|
||||||
<button @click="handleSimpan" class="bg-C text-white px-6 py-2 rounded">
|
<button @click="handleSimpan" :disabled="isSaving" class="bg-C text-white px-6 py-2 rounded disabled:opacity-50">
|
||||||
Simpan
|
{{ isSaving ? 'Menyimpan...' : 'Simpan' }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -162,13 +154,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Simple Toast Alert -->
|
|
||||||
<div v-if="showToast"
|
<div v-if="showToast"
|
||||||
class="fixed top-4 left-1/2 transform -translate-x-1/2 z-[10001]
|
class="fixed top-4 left-1/2 transform -translate-x-1/2 z-[10001]
|
||||||
transition-all duration-300 ease-in-out"
|
transition-all duration-300 ease-in-out"
|
||||||
:class="toastClasses">
|
:class="toastClasses">
|
||||||
<div class="flex items-center gap-2 px-4 py-3 rounded-lg shadow-lg max-w-sm">
|
<div class="flex items-center gap-2 px-4 py-3 rounded-lg shadow-lg max-w-sm">
|
||||||
<!-- Icon -->
|
|
||||||
<div class="flex-shrink-0">
|
<div class="flex-shrink-0">
|
||||||
<svg v-if="toastType === 'error'" class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
<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" />
|
<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" />
|
||||||
@ -180,8 +170,6 @@
|
|||||||
<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" />
|
<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>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Message -->
|
|
||||||
<p class="text-sm font-medium">{{ toastMessage }}</p>
|
<p class="text-sm font-medium">{{ toastMessage }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -198,7 +186,6 @@ import logo_visa from '@/../images/logo_visa.png'
|
|||||||
import logo_mandiri from '@/../images/logo_mandiri.png'
|
import logo_mandiri from '@/../images/logo_mandiri.png'
|
||||||
import inputField from '@/components/InputField.vue'
|
import inputField from '@/components/InputField.vue'
|
||||||
import inputSelect from '@/components/InputSelect.vue'
|
import inputSelect from '@/components/InputSelect.vue'
|
||||||
|
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@ -216,7 +203,7 @@ const props = defineProps({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['close', 'confirm'])
|
const emit = defineEmits(['close', 'confirm', 'transaksi-saved'])
|
||||||
|
|
||||||
const namaPembeli = ref('')
|
const namaPembeli = ref('')
|
||||||
const nomorTelepon = ref('')
|
const nomorTelepon = ref('')
|
||||||
@ -225,10 +212,10 @@ const ongkosBikin = ref(0)
|
|||||||
const selectedSales = ref(null)
|
const selectedSales = ref(null)
|
||||||
const salesOptions = ref([])
|
const salesOptions = ref([])
|
||||||
const ongkosBikinFormatted = ref("")
|
const ongkosBikinFormatted = ref("")
|
||||||
|
const isSaving = ref(false)
|
||||||
|
|
||||||
// Simple Toast State
|
|
||||||
const showToast = ref(false)
|
const showToast = ref(false)
|
||||||
const toastType = ref('error') // 'error', 'success', 'info'
|
const toastType = ref('error')
|
||||||
const toastMessage = ref('')
|
const toastMessage = ref('')
|
||||||
|
|
||||||
const toastClasses = computed(() => {
|
const toastClasses = computed(() => {
|
||||||
@ -245,30 +232,25 @@ const grandTotal = computed(() => {
|
|||||||
return props.total + (ongkosBikin.value || 0)
|
return props.total + (ongkosBikin.value || 0)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Fungsi untuk menentukan style row berdasarkan jumlah item
|
|
||||||
const getRowStyle = () => {
|
const getRowStyle = () => {
|
||||||
if (props.pesanan.length === 1) {
|
if (props.pesanan.length === 1) {
|
||||||
return { height: '126px' } // 2x lipat dari tinggi normal (48px)
|
return { height: '126px' }
|
||||||
}
|
}
|
||||||
return { height: '63px' } // Tinggi normal
|
return { height: '63px' }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Fungsi untuk menentukan class gambar berdasarkan jumlah item
|
|
||||||
const getImageClass = () => {
|
const getImageClass = () => {
|
||||||
if (props.pesanan.length === 1) {
|
if (props.pesanan.length === 1) {
|
||||||
return 'w-25 h-25' // 2x lipat dari ukuran normal (w-10 h-10)
|
return 'w-25 h-25'
|
||||||
}
|
}
|
||||||
return 'w-12 h-12' // Ukuran normal
|
return 'w-12 h-12'
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fungsi untuk menentukan class text berdasarkan jumlah item
|
|
||||||
const getTextClass = () => {
|
const getTextClass = () => {
|
||||||
if (props.pesanan.length === 1) {
|
if (props.pesanan.length === 1) {
|
||||||
return 'text-lg font-medium' // Text lebih besar untuk single item
|
return 'text-lg font-medium'
|
||||||
}
|
}
|
||||||
return 'text-sm' // Text normal
|
return 'text-sm'
|
||||||
}
|
}
|
||||||
|
|
||||||
const getCurrentDate = () => {
|
const getCurrentDate = () => {
|
||||||
@ -290,7 +272,6 @@ const generateTransactionCode = () => {
|
|||||||
return `TRS-${timestamp}`
|
return `TRS-${timestamp}`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simple Toast Function
|
|
||||||
const showSimpleToast = (type, message, duration = 3000) => {
|
const showSimpleToast = (type, message, duration = 3000) => {
|
||||||
toastType.value = type
|
toastType.value = type
|
||||||
toastMessage.value = message
|
toastMessage.value = message
|
||||||
@ -348,14 +329,16 @@ const handleSimpan = () => {
|
|||||||
nama_pembeli: namaPembeli.value,
|
nama_pembeli: namaPembeli.value,
|
||||||
no_hp: nomorTelepon.value,
|
no_hp: nomorTelepon.value,
|
||||||
alamat: alamat.value,
|
alamat: alamat.value,
|
||||||
ongkos_bikin: ongkosBikin.value || 0, // Pastikan nama field benar
|
ongkos_bikin: ongkosBikin.value || 0,
|
||||||
total_harga: grandTotal.value,
|
total_harga: grandTotal.value,
|
||||||
items: props.pesanan
|
items: props.pesanan
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const simpanTransaksi = async (dataTransaksi) => {
|
const simpanTransaksi = async (dataTransaksi) => {
|
||||||
// console.log('Data transaksi yang akan disimpan:', dataTransaksi);
|
if (isSaving.value) return
|
||||||
|
|
||||||
|
isSaving.value = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await axios.post('/api/transaksi', dataTransaksi, {
|
const response = await axios.post('/api/transaksi', dataTransaksi, {
|
||||||
@ -366,16 +349,18 @@ const simpanTransaksi = async (dataTransaksi) => {
|
|||||||
|
|
||||||
showSimpleToast('success', 'Transaksi berhasil disimpan!', 2000)
|
showSimpleToast('success', 'Transaksi berhasil disimpan!', 2000)
|
||||||
|
|
||||||
// Delay untuk memberikan waktu user membaca notifikasi
|
// Emit event dengan data transaksi yang sudah disimpan
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
emit('transaksi-saved', response.data);
|
||||||
emit('close');
|
emit('close');
|
||||||
window.location.reload();
|
|
||||||
}, 2200);
|
}, 2200);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error saving transaksi:', error);
|
console.error('Error saving transaksi:', error);
|
||||||
const errorMessage = error.response?.data?.message || error.message || 'Terjadi kesalahan saat menyimpan transaksi';
|
const errorMessage = error.response?.data?.message || error.message || 'Terjadi kesalahan saat menyimpan transaksi';
|
||||||
showSimpleToast('error', `Error: ${errorMessage}`, 4000);
|
showSimpleToast('error', `Error: ${errorMessage}`, 4000);
|
||||||
|
} finally {
|
||||||
|
isSaving.value = false
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -73,19 +73,16 @@
|
|||||||
<span v-if="item.harga_deal && item.harga_deal > 0">1</span>
|
<span v-if="item.harga_deal && item.harga_deal > 0">1</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="flex items-center gap-2 p-2 border-r border-D" :style="getRowStyle()">
|
<td class="flex items-center gap-2 p-2 border-r border-D" :style="getRowStyle()">
|
||||||
<template v-if="item.produk?.foto?.[0]?.url">
|
<img
|
||||||
<img :src="item.produk.foto[0].url" :class="getImageClass()" class="object-cover rounded" />
|
:src="item.produk?.foto?.[0]?.url || 'https://via.placeholder.com/50x50?text=No+Img'"
|
||||||
</template>
|
:class="getImageClass()"
|
||||||
<template v-else-if="item.produk?.nama">
|
class="object-cover rounded"
|
||||||
<div :class="getImageClass() + ' bg-gray-200 rounded flex items-center justify-center'">
|
/>
|
||||||
<span class="text-xs text-gray-500">IMG</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
<span :class="getTextClass()">{{ item.produk?.nama || '' }}</span>
|
||||||
<template v-else>
|
</td>
|
||||||
<div :class="getImageClass()"></div>
|
|
||||||
</template>
|
|
||||||
<span :class="getTextClass()">{{ item.produk?.nama || '' }}</span>
|
|
||||||
</td>
|
|
||||||
<td class="border-r border-D">
|
<td class="border-r border-D">
|
||||||
<span v-if="item.produk?.nama">{{ item.posisi_asal || 'Brankas' }}</span>
|
<span v-if="item.produk?.nama">{{ item.posisi_asal || 'Brankas' }}</span>
|
||||||
</td>
|
</td>
|
||||||
@ -206,7 +203,7 @@ const formatDate = (dateString) => {
|
|||||||
const day = String(date.getDate()).padStart(2, '0')
|
const day = String(date.getDate()).padStart(2, '0')
|
||||||
const month = months[date.getMonth()]
|
const month = months[date.getMonth()]
|
||||||
const year = date.getFullYear()
|
const year = date.getFullYear()
|
||||||
return `${dayName}/${day}-${month}-${year}`
|
return `${dayName}, ${day}-${month}-${year}`
|
||||||
}
|
}
|
||||||
|
|
||||||
const itemsWithMinimal = computed(() => {
|
const itemsWithMinimal = computed(() => {
|
||||||
|
|||||||
@ -4,7 +4,6 @@
|
|||||||
<div
|
<div
|
||||||
class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-3 sm:gap-2 mx-auto min-h-[75vh]"
|
class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-3 sm:gap-2 mx-auto min-h-[75vh]"
|
||||||
>
|
>
|
||||||
|
|
||||||
<div class="lg:col-span-3">
|
<div class="lg:col-span-3">
|
||||||
<div
|
<div
|
||||||
class="bg-white rounded-xl shadow-lg border border-gray-200 overflow-hidden h-auto lg:h-full"
|
class="bg-white rounded-xl shadow-lg border border-gray-200 overflow-hidden h-auto lg:h-full"
|
||||||
@ -15,13 +14,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="lg:col-span-2">
|
<div class="lg:col-span-2">
|
||||||
<div
|
<div
|
||||||
class="bg-white rounded-xl shadow-lg border border-gray-200 overflow-hidden lg:h-fit sticky top-4 max-h-[70vh] overflow-y-auto"
|
class="bg-white rounded-xl shadow-lg border border-gray-200 overflow-hidden lg:h-fit sticky top-4 max-h-[70vh] overflow-y-auto"
|
||||||
>
|
>
|
||||||
<div class="p-3 sm:p-4 md:p-6">
|
<div class="p-3 sm:p-4 md:p-6">
|
||||||
|
|
||||||
<KasirTransaksiList
|
<KasirTransaksiList
|
||||||
:transaksi="transaksi.data || []"
|
:transaksi="transaksi.data || []"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
@ -34,7 +31,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<ModalConfirm
|
<ModalConfirm
|
||||||
v-if="showConfirm"
|
v-if="showConfirm"
|
||||||
title="Konfirmasi"
|
title="Konfirmasi"
|
||||||
@ -66,13 +62,11 @@ const showConfirm = ref(false);
|
|||||||
const confirmMessage = ref("Apakah kamu yakin?");
|
const confirmMessage = ref("Apakah kamu yakin?");
|
||||||
let lastTransaksi = null;
|
let lastTransaksi = null;
|
||||||
|
|
||||||
|
|
||||||
const handleConfirm = () => {
|
const handleConfirm = () => {
|
||||||
showConfirm.value = false;
|
showConfirm.value = false;
|
||||||
console.log("User konfirmasi, cetak struk di sini...", lastTransaksi);
|
console.log("User konfirmasi, cetak struk di sini...", lastTransaksi);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const fetchTransaksiHariIni = async (page = 1) => {
|
const fetchTransaksiHariIni = async (page = 1) => {
|
||||||
try {
|
try {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
@ -105,47 +99,54 @@ const fetchTransaksiHariIni = async (page = 1) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const handlePageChange = (page) => {
|
const handlePageChange = (page) => {
|
||||||
if (page >= 1 && page <= (transaksi.value.pagination?.last_page || 1)) {
|
if (page >= 1 && page <= (transaksi.value.pagination?.last_page || 1)) {
|
||||||
fetchTransaksiHariIni(page);
|
fetchTransaksiHariIni(page);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const handleTransaksiSaved = async (newTransaksi) => {
|
const handleTransaksiSaved = async (newTransaksi) => {
|
||||||
|
// Format data transaksi baru
|
||||||
const formattedNewTransaksi = {
|
const formattedNewTransaksi = {
|
||||||
id: newTransaksi.id,
|
id: newTransaksi.id,
|
||||||
kode_transaksi: newTransaksi.kode_transaksi,
|
kode_transaksi: newTransaksi.kode_transaksi,
|
||||||
created_at: newTransaksi.created_at,
|
created_at: newTransaksi.created_at,
|
||||||
total_harga: newTransaksi.total_harga || 0,
|
total_harga: newTransaksi.total_harga || 0,
|
||||||
itemTransaksi: newTransaksi.itemTransaksi || [],
|
itemTransaksi: newTransaksi.itemTransaksi || newTransaksi.items || [],
|
||||||
pendapatan: newTransaksi.total_harga || 0,
|
pendapatan: newTransaksi.total_harga || 0,
|
||||||
total_items: newTransaksi.itemTransaksi?.length || 0,
|
total_items: (newTransaksi.itemTransaksi || newTransaksi.items || []).length,
|
||||||
tanggal: new Date(newTransaksi.created_at).toLocaleDateString('id-ID')
|
tanggal: new Date(newTransaksi.created_at).toLocaleDateString('id-ID'),
|
||||||
|
nama_pembeli: newTransaksi.nama_pembeli,
|
||||||
|
alamat: newTransaksi.alamat,
|
||||||
|
no_hp: newTransaksi.no_hp,
|
||||||
|
ongkos_bikin: newTransaksi.ongkos_bikin,
|
||||||
|
nama_sales: newTransaksi.nama_sales
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Tambahkan ke awal list
|
||||||
transaksi.value.data.unshift(formattedNewTransaksi);
|
transaksi.value.data.unshift(formattedNewTransaksi);
|
||||||
lastTransaksi = formattedNewTransaksi;
|
lastTransaksi = formattedNewTransaksi;
|
||||||
|
|
||||||
|
// Update pagination
|
||||||
if (transaksi.value.pagination) {
|
if (transaksi.value.pagination) {
|
||||||
transaksi.value.pagination.total += 1;
|
transaksi.value.pagination.total += 1;
|
||||||
|
// Hapus item terakhir jika melebihi limit
|
||||||
if (transaksi.value.data.length > limit) {
|
if (transaksi.value.data.length > limit) {
|
||||||
transaksi.value.data.pop();
|
transaksi.value.data.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
confirmMessage.value = "Transaksi berhasil disimpan. Cetak struk sekarang?";
|
// Tidak perlu modal konfirmasi lagi karena sudah ada StrukView
|
||||||
showConfirm.value = true;
|
// confirmMessage.value = "Transaksi berhasil disimpan. Cetak struk sekarang?";
|
||||||
|
// showConfirm.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
let refreshInterval = null;
|
let refreshInterval = null;
|
||||||
const startAutoRefresh = () => {
|
const startAutoRefresh = () => {
|
||||||
if (refreshInterval) clearInterval(refreshInterval);
|
if (refreshInterval) clearInterval(refreshInterval);
|
||||||
refreshInterval = setInterval(() => {
|
refreshInterval = setInterval(() => {
|
||||||
fetchTransaksiHariIni(currentPage.value);
|
fetchTransaksiHariIni(currentPage.value);
|
||||||
}, 10000);
|
}, 30000); // Refresh setiap 30 detik (sebelumnya 10 detik)
|
||||||
};
|
};
|
||||||
|
|
||||||
const stopAutoRefresh = () => {
|
const stopAutoRefresh = () => {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user