1714 lines
76 KiB
PHP
1714 lines
76 KiB
PHP
@extends('layouts.dashboard')
|
||
|
||
@section('content')
|
||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||
<style>
|
||
.item-li {
|
||
transition: all 0.3s ease;
|
||
}
|
||
</style>
|
||
<script>
|
||
// Setup CSRF token untuk semua request Ajax
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
// Get CSRF token dari meta tag
|
||
var token = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
|
||
|
||
// Setup global headers untuk semua Fetch API request
|
||
window.originalFetch = window.fetch;
|
||
window.fetch = function(url, options) {
|
||
options = options || {};
|
||
options.headers = options.headers || {};
|
||
options.headers['X-CSRF-TOKEN'] = token;
|
||
return window.originalFetch(url, options);
|
||
};
|
||
|
||
console.log('CSRF token setup untuk semua request Ajax');
|
||
});
|
||
|
||
// Format angka sebagai Rupiah
|
||
function formatRupiah(angka) {
|
||
// Pastikan angka adalah tipe number
|
||
angka = typeof angka === 'string' ? parseInt(angka.replace(/[^\d]/g, '')) : angka;
|
||
|
||
// Jika bukan angka, return 0
|
||
if (isNaN(angka)) angka = 0;
|
||
|
||
return new Intl.NumberFormat('id-ID').format(angka);
|
||
}
|
||
|
||
// Update total dari item yang dipilih
|
||
function updateTotal() {
|
||
// Ambil semua checkbox yang dicentang
|
||
var checkboxes = document.querySelectorAll('.item-checkbox:checked');
|
||
var count = checkboxes.length;
|
||
var total = 0;
|
||
|
||
console.log('Update total untuk', count, 'item');
|
||
|
||
// Hitung total harga
|
||
for (var i = 0; i < checkboxes.length; i++) {
|
||
var checkbox = checkboxes[i];
|
||
|
||
// Ambil nilai jumlah dari input yang terkait
|
||
var listItem = checkbox.closest('li');
|
||
var jumlahInput = listItem.querySelector('input[name="jumlah"]');
|
||
var jumlah = jumlahInput ? parseInt(jumlahInput.value) : parseInt(checkbox.getAttribute('data-jumlah') || 1);
|
||
|
||
// Ambil harga satuan dari atribut data-harga-satuan
|
||
var hargaSatuan = parseInt(checkbox.getAttribute('data-harga-satuan') || 0);
|
||
|
||
// Hitung total harga item (harga satuan × jumlah)
|
||
var itemTotal = hargaSatuan * jumlah;
|
||
|
||
console.log('Item', i+1, 'Jumlah:', jumlah, 'Harga Satuan:', hargaSatuan, 'Total Item:', itemTotal);
|
||
total += itemTotal;
|
||
|
||
// Update data-price agar konsisten dengan jumlah saat ini
|
||
checkbox.setAttribute('data-price', itemTotal);
|
||
}
|
||
|
||
console.log('Total harga semua item:', total);
|
||
|
||
// Update tampilan subtotal
|
||
document.getElementById('selected-count').textContent = count;
|
||
document.getElementById('selected-total').textContent = formatRupiah(total);
|
||
document.getElementById('checkout-count').textContent = count;
|
||
|
||
// Update tombol checkout
|
||
var checkoutBtn = document.getElementById('checkout-button');
|
||
if (count > 0) {
|
||
checkoutBtn.disabled = false;
|
||
checkoutBtn.classList.remove('bg-gray-400', 'cursor-not-allowed');
|
||
checkoutBtn.classList.add('bg-[#2C7A7B]', 'hover:bg-[#1C6B6B]');
|
||
} else {
|
||
checkoutBtn.disabled = true;
|
||
checkoutBtn.classList.add('bg-gray-400', 'cursor-not-allowed');
|
||
checkoutBtn.classList.remove('bg-[#2C7A7B]', 'hover:bg-[#1C6B6B]');
|
||
}
|
||
|
||
// Update tombol hapus
|
||
var deleteBtn = document.getElementById('delete-selected');
|
||
if (count > 0) {
|
||
deleteBtn.style.display = 'block';
|
||
deleteBtn.classList.remove('hidden');
|
||
} else {
|
||
deleteBtn.style.display = 'none';
|
||
deleteBtn.classList.add('hidden');
|
||
}
|
||
|
||
// Update kondisi checkbox "Pilih Semua"
|
||
var selectAll = document.getElementById('select-all');
|
||
var allCheckboxes = document.querySelectorAll('.item-checkbox');
|
||
selectAll.checked = (allCheckboxes.length > 0 && count === allCheckboxes.length);
|
||
}
|
||
|
||
// Pilih atau batalkan pilih semua item
|
||
function pilihSemua() {
|
||
var isChecked = document.getElementById('select-all').checked;
|
||
var checkboxes = document.querySelectorAll('.item-checkbox');
|
||
|
||
for (var i = 0; i < checkboxes.length; i++) {
|
||
checkboxes[i].checked = isChecked;
|
||
}
|
||
|
||
updateTotal();
|
||
}
|
||
|
||
// Tambah jumlah item
|
||
function tambahJumlah(btn, event) {
|
||
if (event) event.preventDefault();
|
||
|
||
var form = btn.closest('form');
|
||
var input = form.querySelector('input[name="jumlah"]');
|
||
var maxStok = parseInt(form.querySelector('input[name="max_stok"]').value);
|
||
var currentValue = parseInt(input.value);
|
||
|
||
// Nonaktifkan tombol jika sudah mencapai batas
|
||
var minusBtn = form.querySelector('button:first-of-type');
|
||
var plusBtn = form.querySelector('button:last-of-type');
|
||
|
||
if (currentValue < maxStok) {
|
||
// Tambahkan nilai langsung
|
||
input.value = currentValue + 1;
|
||
|
||
// Update status tombol sebelum submit
|
||
minusBtn.disabled = false;
|
||
minusBtn.classList.remove('opacity-50');
|
||
|
||
if (currentValue + 1 >= maxStok) {
|
||
plusBtn.disabled = true;
|
||
plusBtn.classList.add('opacity-50');
|
||
}
|
||
|
||
// Update data-jumlah pada checkbox sebelum submit
|
||
var listItem = form.closest('li');
|
||
var checkbox = listItem.querySelector('.item-checkbox');
|
||
if (checkbox) {
|
||
checkbox.setAttribute('data-jumlah', currentValue + 1);
|
||
// Jika checkbox tercentang, perbarui total sementara untuk UI responsif
|
||
if (checkbox.checked) {
|
||
var hargaSatuan = parseInt(checkbox.getAttribute('data-harga-satuan') || 0);
|
||
var newTotalPrice = hargaSatuan * (currentValue + 1);
|
||
checkbox.setAttribute('data-price', newTotalPrice);
|
||
|
||
// Update total tampilan segera sebelum submit
|
||
updateTotal();
|
||
}
|
||
}
|
||
|
||
try {
|
||
// Simpan dengan cara tradisional via iframe
|
||
submitFormViaIframe(form);
|
||
} catch (e) {
|
||
console.error('Error submitFormViaIframe:', e);
|
||
// Gunakan metode alternatif jika iframe gagal
|
||
updateJumlahDirect(form);
|
||
}
|
||
}
|
||
}
|
||
|
||
// Kurangi jumlah item
|
||
function kurangJumlah(btn, event) {
|
||
if (event) event.preventDefault();
|
||
|
||
var form = btn.closest('form');
|
||
var input = form.querySelector('input[name="jumlah"]');
|
||
var currentValue = parseInt(input.value);
|
||
|
||
// Nonaktifkan tombol jika sudah mencapai batas
|
||
var minusBtn = form.querySelector('button:first-of-type');
|
||
var plusBtn = form.querySelector('button:last-of-type');
|
||
|
||
if (currentValue > 1) {
|
||
// Kurangi nilai langsung
|
||
input.value = currentValue - 1;
|
||
|
||
// Update status tombol sebelum submit
|
||
plusBtn.disabled = false;
|
||
plusBtn.classList.remove('opacity-50');
|
||
|
||
if (currentValue - 1 <= 1) {
|
||
minusBtn.disabled = true;
|
||
minusBtn.classList.add('opacity-50');
|
||
}
|
||
|
||
// Update data-jumlah pada checkbox sebelum submit
|
||
var listItem = form.closest('li');
|
||
var checkbox = listItem.querySelector('.item-checkbox');
|
||
if (checkbox) {
|
||
checkbox.setAttribute('data-jumlah', currentValue - 1);
|
||
// Jika checkbox tercentang, perbarui total sementara untuk UI responsif
|
||
if (checkbox.checked) {
|
||
var hargaSatuan = parseInt(checkbox.getAttribute('data-harga-satuan') || 0);
|
||
var newTotalPrice = hargaSatuan * (currentValue - 1);
|
||
checkbox.setAttribute('data-price', newTotalPrice);
|
||
|
||
// Update total tampilan segera sebelum submit
|
||
updateTotal();
|
||
}
|
||
}
|
||
|
||
try {
|
||
// Simpan dengan cara tradisional via iframe
|
||
submitFormViaIframe(form);
|
||
} catch (e) {
|
||
console.error('Error submitFormViaIframe:', e);
|
||
// Gunakan metode alternatif jika iframe gagal
|
||
updateJumlahDirect(form);
|
||
}
|
||
}
|
||
}
|
||
|
||
// Fungsi alternatif untuk update jumlah dengan metode direct API
|
||
function updateJumlahDirect(form) {
|
||
// Tampilkan notifikasi
|
||
// showNotification('Menggunakan metode alternatif...', 'success');
|
||
|
||
// Ambil data dari form
|
||
var itemId = form.getAttribute('id').replace('form-update-', '');
|
||
var jumlahInput = form.querySelector('input[name="jumlah"]');
|
||
var jumlah = jumlahInput.value;
|
||
|
||
console.log('Update direct: ID=' + itemId + ', jumlah=' + jumlah);
|
||
|
||
// Tampilkan efek loading
|
||
var listItem = form.closest('li');
|
||
var totalElement = listItem.querySelector('.total-harga');
|
||
|
||
if (totalElement) {
|
||
totalElement.classList.add('opacity-50');
|
||
// totalElement.innerHTML = '<span class="inline-block animate-pulse">Menyimpan...</span>';
|
||
}
|
||
|
||
// Dapatkan CSRF token
|
||
var csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
|
||
|
||
// Buat FormData untuk request
|
||
var formData = new FormData();
|
||
formData.append('id', itemId);
|
||
formData.append('jumlah', jumlah);
|
||
formData.append('_token', csrfToken);
|
||
formData.append('_ts', new Date().getTime());
|
||
|
||
// Kirim dengan fetch API
|
||
fetch("{{ route('keranjang.update.direct') }}", {
|
||
method: 'POST',
|
||
headers: {
|
||
'X-CSRF-TOKEN': csrfToken,
|
||
'X-Requested-With': 'XMLHttpRequest',
|
||
'Accept': 'application/json'
|
||
},
|
||
body: formData
|
||
})
|
||
.then(response => {
|
||
console.log('Response status:', response.status);
|
||
|
||
if (!response.ok) {
|
||
console.error('Response error:', response.status, response.statusText);
|
||
throw new Error('Status: ' + response.status);
|
||
}
|
||
return response.json();
|
||
})
|
||
.then(data => {
|
||
console.log('Respons dari server:', data);
|
||
|
||
if (data.success) {
|
||
// Update UI
|
||
if (totalElement) {
|
||
totalElement.classList.remove('opacity-50');
|
||
totalElement.innerHTML = 'Rp ' + formatRupiah(data.data.total_harga);
|
||
totalElement.classList.add('text-green-600');
|
||
|
||
// Hapus highlight setelah beberapa saat
|
||
setTimeout(function() {
|
||
totalElement.classList.remove('text-green-600');
|
||
}, 1000);
|
||
}
|
||
|
||
// Update data pada checkbox
|
||
var checkbox = listItem.querySelector('.item-checkbox');
|
||
if (checkbox) {
|
||
checkbox.setAttribute('data-price', data.data.total_harga);
|
||
checkbox.setAttribute('data-jumlah', data.data.jumlah);
|
||
|
||
// Update total jika checkbox dicentang
|
||
if (checkbox.checked) {
|
||
updateTotal();
|
||
}
|
||
}
|
||
|
||
// Perbarui nilai original
|
||
jumlahInput.dataset.originalValue = data.data.jumlah;
|
||
|
||
// Tampilkan notifikasi sukses
|
||
// showNotification('Jumlah berhasil diperbarui', 'success');
|
||
} else {
|
||
// Tampilkan pesan error
|
||
totalElement.classList.remove('opacity-50');
|
||
totalElement.innerHTML = 'Error';
|
||
// showNotification(data.message || 'Gagal memperbarui jumlah', 'error');
|
||
|
||
// Kembalikan nilai sebelumnya
|
||
jumlahInput.value = jumlahInput.dataset.originalValue || 1;
|
||
updateJumlahInput(jumlahInput);
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Error:', error);
|
||
|
||
// Kembalikan tampilan ke normal
|
||
if (totalElement) {
|
||
totalElement.classList.remove('opacity-50');
|
||
totalElement.innerHTML = 'Error';
|
||
}
|
||
|
||
// Kembalikan nilai sebelumnya
|
||
jumlahInput.value = jumlahInput.dataset.originalValue || 1;
|
||
updateJumlahInput(jumlahInput);
|
||
|
||
// Tampilkan notifikasi error dan reload sebagai fallback terakhir
|
||
// showNotification('Gagal memperbarui jumlah. Halaman akan dimuat ulang.', 'error');
|
||
setTimeout(function() {
|
||
window.location.reload();
|
||
}, 2000);
|
||
});
|
||
}
|
||
|
||
// Fungsi untuk submit form via iframe tersembunyi
|
||
function submitFormViaIframe(form, onSubmitSuccess) {
|
||
// Tampilkan efek loading
|
||
var listItem = form.closest('li');
|
||
var totalElement = listItem.querySelector('.total-harga');
|
||
|
||
if (totalElement) {
|
||
totalElement.classList.add('opacity-50');
|
||
// totalElement.innerHTML = '<span class="inline-block animate-pulse">Menyimpan...</span>';
|
||
}
|
||
|
||
// Tambahkan waktu submit untuk menghindari cache
|
||
var timeStamp = new Date().getTime();
|
||
var uniqueField = document.createElement('input');
|
||
uniqueField.type = 'hidden';
|
||
uniqueField.name = '_ts';
|
||
uniqueField.value = timeStamp;
|
||
form.appendChild(uniqueField);
|
||
|
||
// Tambahkan flag untuk menunjukkan permintaan dari JavaScript
|
||
var jsField = document.createElement('input');
|
||
jsField.type = 'hidden';
|
||
jsField.name = 'is_js_request';
|
||
jsField.value = '1';
|
||
form.appendChild(jsField);
|
||
|
||
// Tambahkan CSRF token
|
||
var csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
|
||
var csrfField = document.createElement('input');
|
||
csrfField.type = 'hidden';
|
||
csrfField.name = '_token';
|
||
csrfField.value = csrfToken;
|
||
form.appendChild(csrfField);
|
||
|
||
// Tambahkan atau perbarui field _method dengan PUT
|
||
var methodField = form.querySelector('input[name="_method"]');
|
||
if (!methodField) {
|
||
methodField = document.createElement('input');
|
||
methodField.type = 'hidden';
|
||
methodField.name = '_method';
|
||
methodField.value = 'PUT';
|
||
form.appendChild(methodField);
|
||
} else {
|
||
methodField.value = 'PUT';
|
||
}
|
||
|
||
// Tandai item yang sedang diproses dengan atribut sementara
|
||
var listItem = form.closest('li');
|
||
if (listItem) {
|
||
listItem.setAttribute('data-processing', 'true');
|
||
}
|
||
|
||
// Cek apakah iframe untuk submit sudah ada
|
||
var iframeId = 'hidden-submit-frame-' + timeStamp;
|
||
var iframe = document.createElement('iframe');
|
||
iframe.setAttribute('id', iframeId);
|
||
iframe.setAttribute('name', iframeId);
|
||
iframe.style.display = 'none';
|
||
document.body.appendChild(iframe);
|
||
|
||
// Setup penanganan pesan dari iframe
|
||
window.addEventListener('message', function(event) {
|
||
if (event.data === 'update_success' || event.data === 'delete_success') {
|
||
console.log('Received success message from iframe:', event.data);
|
||
|
||
// Update UI jika sukses
|
||
if (onSuccessCallback) {
|
||
onSuccessCallback();
|
||
}
|
||
}
|
||
}, false);
|
||
|
||
// Variable untuk menyimpan callback
|
||
var onSuccessCallback = null;
|
||
|
||
// Timeout handler untuk menangani iframe yang tidak merespons
|
||
var timeoutId = setTimeout(function() {
|
||
console.error('Iframe timeout - no response received');
|
||
|
||
// Hapus iframe
|
||
if (iframe && iframe.parentNode) {
|
||
iframe.parentNode.removeChild(iframe);
|
||
}
|
||
|
||
// Coba dengan metode direct
|
||
console.log('Trying alternative update method...');
|
||
updateJumlahDirect(form);
|
||
|
||
}, 5000); // Tunggu 5 detik
|
||
|
||
// Tambahkan event listener untuk respons iframe
|
||
iframe.onload = function() {
|
||
// Batalkan timeout
|
||
clearTimeout(timeoutId);
|
||
|
||
// Ini akan dijalankan setelah iframe menerima respons
|
||
console.log('Iframe loaded, checking response...');
|
||
|
||
try {
|
||
// Coba dapatkan respons dari iframe
|
||
var iframeContent = iframe.contentDocument || iframe.contentWindow.document;
|
||
console.log('Iframe response received');
|
||
|
||
// Periksa respons untuk kesalahan
|
||
if (iframeContent.body.textContent.includes('403') ||
|
||
iframeContent.body.textContent.includes('Forbidden') ||
|
||
iframeContent.body.textContent.includes('error')) {
|
||
|
||
console.error('Error dari iframe:', iframeContent.body.textContent);
|
||
|
||
// Kirim ulang dengan metode direct
|
||
// showNotification('Terjadi kesalahan, mencoba metode alternatif...', 'error');
|
||
updateJumlahDirect(form);
|
||
return;
|
||
}
|
||
|
||
// Cari elemen terkait
|
||
var listItem = form.closest('li');
|
||
var totalElement = listItem.querySelector('.total-harga');
|
||
var checkbox = listItem.querySelector('.item-checkbox');
|
||
var jumlahInput = form.querySelector('input[name="jumlah"]');
|
||
|
||
// Menyiapkan callback untuk eksekusi
|
||
onSuccessCallback = function() {
|
||
if (jumlahInput && checkbox && totalElement) {
|
||
// Perbarui ke server kembali dengan submit normal untuk memastikan data tersimpan
|
||
setTimeout(function() {
|
||
// Perbarui ke server kembali dengan submit normal
|
||
var reconfirmForm = document.createElement('form');
|
||
reconfirmForm.method = 'POST';
|
||
reconfirmForm.action = form.getAttribute('action');
|
||
|
||
// Salin semua input dari form asli
|
||
var inputs = form.querySelectorAll('input');
|
||
for (var i = 0; i < inputs.length; i++) {
|
||
var input = document.createElement('input');
|
||
input.name = inputs[i].name;
|
||
input.value = inputs[i].value;
|
||
input.type = 'hidden';
|
||
reconfirmForm.appendChild(input);
|
||
}
|
||
|
||
// Tambahkan tanda bahwa ini adalah pengiriman kedua untuk konfirmasi
|
||
var confirmField = document.createElement('input');
|
||
confirmField.type = 'hidden';
|
||
confirmField.name = 'is_confirm_submit';
|
||
confirmField.value = '1';
|
||
reconfirmForm.appendChild(confirmField);
|
||
|
||
// Tambahkan CSRF token baru
|
||
var csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
|
||
var csrfInput = document.createElement('input');
|
||
csrfInput.type = 'hidden';
|
||
csrfInput.name = '_token';
|
||
csrfInput.value = csrfToken;
|
||
reconfirmForm.appendChild(csrfInput);
|
||
|
||
try {
|
||
// Kirim ke rute biasa tanpa menggunakan target iframe
|
||
document.body.appendChild(reconfirmForm);
|
||
reconfirmForm.submit();
|
||
|
||
// Tampilkan pesan notifikasi
|
||
// showNotification('Menyimpan perubahan...', 'success');
|
||
|
||
// Hitung total baru berdasarkan harga satuan dan jumlah baru
|
||
var hargaSatuan = parseInt(checkbox.getAttribute('data-harga-satuan') || 0);
|
||
var jumlah = parseInt(jumlahInput.value);
|
||
var totalHarga = hargaSatuan * jumlah;
|
||
|
||
// Update tampilan harga sementara
|
||
totalElement.classList.remove('opacity-50');
|
||
totalElement.innerHTML = 'Rp ' + formatRupiah(totalHarga);
|
||
totalElement.classList.add('text-green-600');
|
||
|
||
// Hapus highlight setelah beberapa saat
|
||
setTimeout(function() {
|
||
totalElement.classList.remove('text-green-600');
|
||
|
||
// Hapus tanda pemrosesan
|
||
if (listItem) {
|
||
listItem.removeAttribute('data-processing');
|
||
}
|
||
}, 1000);
|
||
|
||
// Update data pada checkbox
|
||
checkbox.setAttribute('data-price', totalHarga);
|
||
checkbox.setAttribute('data-jumlah', jumlah);
|
||
|
||
// Update total jika checkbox dicentang
|
||
if (checkbox.checked) {
|
||
updateTotal();
|
||
}
|
||
|
||
// Jalankan callback sukses jika ada
|
||
if (onSubmitSuccess) {
|
||
onSubmitSuccess();
|
||
}
|
||
} catch (submitError) {
|
||
console.error('Error saat mengirim form konfirmasi:', submitError);
|
||
updateJumlahDirect(form); // Coba dengan metode direct sebagai fallback
|
||
}
|
||
}, 500); // Tunggu 500ms sebelum mengirim form konfirmasi
|
||
}
|
||
};
|
||
|
||
// Eksekusi callback
|
||
onSuccessCallback();
|
||
|
||
} catch(e) {
|
||
console.error('Error processing iframe response:', e);
|
||
|
||
// Coba dengan metode alternatif
|
||
// showNotification('Terjadi kesalahan, menggunakan metode alternatif...', 'error');
|
||
updateJumlahDirect(form);
|
||
} finally {
|
||
// Hapus iframe setelah selesai
|
||
setTimeout(function() {
|
||
if (iframe && iframe.parentNode) {
|
||
iframe.parentNode.removeChild(iframe);
|
||
}
|
||
}, 1000);
|
||
}
|
||
};
|
||
|
||
try {
|
||
// Set target ke iframe tersembunyi
|
||
form.setAttribute('target', iframeId);
|
||
|
||
// Submit form
|
||
console.log('Submitting form to:', form.getAttribute('action'));
|
||
form.submit();
|
||
} catch (submitError) {
|
||
console.error('Error submitting form via iframe:', submitError);
|
||
clearTimeout(timeoutId);
|
||
|
||
// Hapus iframe jika ada error
|
||
if (iframe && iframe.parentNode) {
|
||
iframe.parentNode.removeChild(iframe);
|
||
}
|
||
|
||
// Coba dengan metode direct
|
||
updateJumlahDirect(form);
|
||
}
|
||
}
|
||
|
||
// Menampilkan error
|
||
function showError(element, originalText) {
|
||
if (element) {
|
||
element.classList.remove('opacity-50');
|
||
element.innerHTML = originalText;
|
||
element.classList.add('text-red-600');
|
||
|
||
setTimeout(function() {
|
||
element.classList.remove('text-red-600');
|
||
}, 2000);
|
||
}
|
||
|
||
// Tampilkan pesan error
|
||
alert('Gagal menyimpan perubahan. Silakan coba lagi.');
|
||
}
|
||
|
||
// Tampilkan modal hapus
|
||
function hapusTerpilih() {
|
||
var selectedCount = document.querySelectorAll('.item-checkbox:checked').length;
|
||
document.getElementById('delete-count').textContent = selectedCount;
|
||
|
||
// Kosongkan container untuk input hidden
|
||
var container = document.getElementById('delete-items-container');
|
||
container.innerHTML = '';
|
||
|
||
// Tambahkan ID item terpilih ke dalam form hapus
|
||
var checkboxes = document.querySelectorAll('.item-checkbox:checked');
|
||
for (var i = 0; i < checkboxes.length; i++) {
|
||
var input = document.createElement('input');
|
||
input.type = 'hidden';
|
||
input.name = 'selected_items[]';
|
||
input.value = checkboxes[i].value;
|
||
container.appendChild(input);
|
||
}
|
||
|
||
// Tampilkan modal
|
||
document.getElementById('delete-modal').classList.remove('hidden');
|
||
}
|
||
|
||
// Tutup modal hapus
|
||
function tutupModal() {
|
||
document.getElementById('delete-modal').classList.add('hidden');
|
||
}
|
||
|
||
// Fungsi untuk melakukan checkout
|
||
function checkoutItems() {
|
||
// Periksa apakah ada item yang dipilih
|
||
var checkboxes = document.querySelectorAll('.item-checkbox:checked');
|
||
if (checkboxes.length === 0) {
|
||
alert('Pilih minimal satu item untuk checkout');
|
||
return false;
|
||
}
|
||
|
||
console.log('Memproses checkout untuk', checkboxes.length, 'item');
|
||
|
||
// Pastikan CSRF token tersedia
|
||
var csrfToken = refreshCsrfToken();
|
||
|
||
// Buat form dinamis untuk mengirimkan data
|
||
var form = document.createElement('form');
|
||
form.method = 'POST';
|
||
form.action = "{{ route('keranjang.checkout-selected') }}";
|
||
form.style.display = 'none';
|
||
|
||
// Tambahkan CSRF token
|
||
var csrfInput = document.createElement('input');
|
||
csrfInput.type = 'hidden';
|
||
csrfInput.name = '_token';
|
||
csrfInput.value = csrfToken;
|
||
form.appendChild(csrfInput);
|
||
|
||
// Tambahkan semua ID yang dipilih ke form beserta jumlahnya
|
||
for (var i = 0; i < checkboxes.length; i++) {
|
||
var checkbox = checkboxes[i];
|
||
var itemId = checkbox.value;
|
||
|
||
// Tambahkan ID item
|
||
var inputId = document.createElement('input');
|
||
inputId.type = 'hidden';
|
||
inputId.name = 'selected_items[]';
|
||
inputId.value = itemId;
|
||
form.appendChild(inputId);
|
||
|
||
// Ambil nilai jumlah dari input terkait
|
||
var listItem = checkbox.closest('li');
|
||
var jumlahInput = listItem.querySelector('input[name="jumlah"]');
|
||
var jumlah = jumlahInput ? jumlahInput.value : 1;
|
||
|
||
// Tambahkan jumlah item
|
||
var inputJumlah = document.createElement('input');
|
||
inputJumlah.type = 'hidden';
|
||
inputJumlah.name = 'jumlah[' + itemId + ']';
|
||
inputJumlah.value = jumlah;
|
||
form.appendChild(inputJumlah);
|
||
|
||
console.log('Checkout item:', itemId, 'jumlah:', jumlah);
|
||
}
|
||
|
||
// Tambahkan form ke body dan kirim
|
||
document.body.appendChild(form);
|
||
console.log('Form checkout dibuat, mengirimkan...');
|
||
form.submit();
|
||
|
||
return true;
|
||
}
|
||
|
||
// Inisialisasi status tombol tambah/kurang
|
||
function initButtonState() {
|
||
var forms = document.querySelectorAll('form[id^="form-update-"]');
|
||
|
||
forms.forEach(function(form) {
|
||
var input = form.querySelector('input[name="jumlah"]');
|
||
var maxStok = parseInt(form.querySelector('input[name="max_stok"]').value);
|
||
var currentValue = parseInt(input.value);
|
||
|
||
// Simpan nilai awal untuk referensi
|
||
input.dataset.originalValue = currentValue;
|
||
|
||
var minusBtn = form.querySelector('button:first-of-type');
|
||
var plusBtn = form.querySelector('button:last-of-type');
|
||
|
||
// Set status tombol kurang
|
||
if (currentValue <= 1) {
|
||
minusBtn.disabled = true;
|
||
minusBtn.classList.add('opacity-50');
|
||
} else {
|
||
minusBtn.disabled = false;
|
||
minusBtn.classList.remove('opacity-50');
|
||
}
|
||
|
||
// Set status tombol tambah
|
||
if (currentValue >= maxStok) {
|
||
plusBtn.disabled = true;
|
||
plusBtn.classList.add('opacity-50');
|
||
} else {
|
||
plusBtn.disabled = false;
|
||
plusBtn.classList.remove('opacity-50');
|
||
}
|
||
});
|
||
}
|
||
|
||
// Fungsi untuk memperbarui UI saat input jumlah diubah langsung
|
||
function updateJumlahInput(input) {
|
||
var form = input.closest('form');
|
||
var maxStok = parseInt(form.querySelector('input[name="max_stok"]').value);
|
||
var currentValue = parseInt(input.value) || 0;
|
||
|
||
// Validasi nilai minimum dan maksimum
|
||
if (currentValue < 1) currentValue = 1;
|
||
if (currentValue > maxStok) currentValue = maxStok;
|
||
|
||
// Update status tombol
|
||
var minusBtn = form.querySelector('button:first-of-type');
|
||
var plusBtn = form.querySelector('button:last-of-type');
|
||
|
||
minusBtn.disabled = (currentValue <= 1);
|
||
minusBtn.classList.toggle('opacity-50', currentValue <= 1);
|
||
|
||
plusBtn.disabled = (currentValue >= maxStok);
|
||
plusBtn.classList.toggle('opacity-50', currentValue >= maxStok);
|
||
|
||
// Update preview harga jika checkbox tercentang
|
||
var listItem = form.closest('li');
|
||
var checkbox = listItem.querySelector('.item-checkbox');
|
||
var totalElement = listItem.querySelector('.total-harga');
|
||
|
||
if (checkbox && checkbox.checked && totalElement) {
|
||
var hargaSatuan = parseInt(checkbox.getAttribute('data-harga-satuan') || 0);
|
||
var newTotalPrice = hargaSatuan * currentValue;
|
||
|
||
// Update atribut data-price pada checkbox
|
||
checkbox.setAttribute('data-price', newTotalPrice);
|
||
checkbox.setAttribute('data-jumlah', currentValue);
|
||
|
||
// Preview harga baru dengan efek 'calculating'
|
||
totalElement.innerHTML = '<span class="text-blue-500">Rp ' + formatRupiah(newTotalPrice) + '</span>';
|
||
|
||
// Update subtotal
|
||
updateTotal();
|
||
}
|
||
}
|
||
|
||
// Fungsi untuk validasi dan pembaruan final saat input kehilangan fokus
|
||
function validateAndUpdate(input) {
|
||
var form = input.closest('form');
|
||
var maxStok = parseInt(form.querySelector('input[name="max_stok"]').value);
|
||
var currentValue = parseInt(input.value) || 0;
|
||
|
||
// Validasi nilai
|
||
if (isNaN(currentValue) || currentValue < 1) {
|
||
input.value = 1;
|
||
currentValue = 1;
|
||
} else if (currentValue > maxStok) {
|
||
input.value = maxStok;
|
||
currentValue = maxStok;
|
||
}
|
||
|
||
// Update preview UI sebelum submit
|
||
updateJumlahInput(input);
|
||
|
||
// Jika nilai tidak berubah, tidak perlu submit
|
||
if (input.dataset.originalValue && parseInt(input.dataset.originalValue) === currentValue) {
|
||
console.log('Nilai tidak berubah, tidak perlu submit');
|
||
return;
|
||
}
|
||
|
||
// Tambahkan callback untuk memperbarui nilai originalValue setelah berhasil
|
||
var onSubmitSuccess = function() {
|
||
// Perbarui nilai originalValue setelah berhasil disimpan
|
||
input.dataset.originalValue = currentValue.toString();
|
||
console.log('Nilai originalValue diperbarui ke', currentValue);
|
||
};
|
||
|
||
try {
|
||
// Submit dengan form biasa via iframe
|
||
submitFormViaIframe(form, onSubmitSuccess);
|
||
} catch (e) {
|
||
console.error('Error saat submit form via iframe:', e);
|
||
// Gunakan metode direct sebagai fallback
|
||
updateJumlahDirect(form);
|
||
}
|
||
}
|
||
|
||
// Fungsi untuk memastikan CSRF token selalu tersedia
|
||
function refreshCsrfToken() {
|
||
// Cek apakah meta tag CSRF sudah ada
|
||
var metaTag = document.querySelector('meta[name="csrf-token"]');
|
||
if (!metaTag) {
|
||
// Jika tidak ada, tambahkan meta tag
|
||
metaTag = document.createElement('meta');
|
||
metaTag.setAttribute('name', 'csrf-token');
|
||
metaTag.setAttribute('content', '{{ csrf_token() }}');
|
||
document.head.appendChild(metaTag);
|
||
console.log('CSRF token meta tag ditambahkan');
|
||
}
|
||
|
||
return metaTag.getAttribute('content');
|
||
}
|
||
|
||
// Fungsi untuk menghapus item menggunakan AJAX
|
||
function hapusItem(btn) {
|
||
// Konfirmasi dulu
|
||
if (!confirm('Yakin ingin menghapus item ini?')) {
|
||
return;
|
||
}
|
||
|
||
var form = btn.closest('form');
|
||
var listItem = form.closest('li');
|
||
var itemId = form.querySelector('input[name="item_id"]').value;
|
||
|
||
console.log('Menghapus item dengan ID:', itemId);
|
||
|
||
// Tampilkan modal loading
|
||
listItem.classList.add('opacity-50');
|
||
// showNotification('Menghapus item ID ' + itemId + '...', 'success');
|
||
|
||
// Dapatkan CSRF token
|
||
var csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
|
||
console.log('CSRF Token:', csrfToken);
|
||
|
||
// Buat data payload sederhana
|
||
var payload = {
|
||
id: itemId,
|
||
timestamp: new Date().getTime()
|
||
};
|
||
console.log('Payload:', payload);
|
||
|
||
// Gunakan fetch API dengan mode debug
|
||
console.log('Mengirim request ke: {{ route("keranjang.hapus.direct") }}');
|
||
fetch("{{ route('keranjang.hapus.direct') }}", {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
'Accept': 'application/json',
|
||
'X-CSRF-TOKEN': csrfToken,
|
||
'X-Requested-With': 'XMLHttpRequest'
|
||
},
|
||
body: JSON.stringify(payload)
|
||
})
|
||
.then(response => {
|
||
console.log('Response status:', response.status);
|
||
|
||
if (!response.ok) {
|
||
console.error('Response error:', response.status, response.statusText);
|
||
|
||
// Jika gagal dengan JSON, coba dengan XMLHttpRequest alternative
|
||
console.log('Mencoba metode alternatif...');
|
||
hapusItemAlt(btn);
|
||
throw new Error('Status: ' + response.status);
|
||
}
|
||
return response.json();
|
||
})
|
||
.then(data => {
|
||
console.log('Respons dari server:', data);
|
||
|
||
if (data.success) {
|
||
// Animasi visual sebelum remove
|
||
listItem.style.height = listItem.offsetHeight + 'px';
|
||
setTimeout(function() {
|
||
listItem.style.height = '0';
|
||
listItem.style.opacity = '0';
|
||
listItem.style.padding = '0';
|
||
listItem.style.margin = '0';
|
||
listItem.style.overflow = 'hidden';
|
||
|
||
// Hapus elemen setelah animasi
|
||
setTimeout(function() {
|
||
listItem.remove();
|
||
updateTotal(); // Update subtotal setelah hapus
|
||
|
||
// Periksa apakah keranjang kosong
|
||
var items = document.querySelectorAll('.item-checkbox');
|
||
if (items.length === 0) {
|
||
// Berikan waktu untuk melihat notifikasi
|
||
setTimeout(function() {
|
||
window.location.reload();
|
||
}, 1000);
|
||
}
|
||
|
||
// Tampilkan notifikasi sukses
|
||
// showNotification('Item berhasil dihapus', 'success');
|
||
}, 300);
|
||
}, 10);
|
||
} else {
|
||
// Tampilkan pesan error
|
||
listItem.classList.remove('opacity-50');
|
||
// showNotification(data.message || 'Gagal menghapus item', 'error');
|
||
console.error('Error dari server:', data);
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Error:', error);
|
||
// Kembalikan tampilan item ke normal jika metode alternatif juga gagal
|
||
// listItem.classList.remove('opacity-50');
|
||
// showNotification('Gagal menghapus item. Silakan coba lagi.', 'error');
|
||
});
|
||
}
|
||
|
||
// Fungsi alternatif untuk menghapus item dengan XMLHttpRequest (lebih kompatibel)
|
||
function hapusItemAlt(btn) {
|
||
// Jika dipanggil langsung (bukan dari hapusItem)
|
||
if (btn && !confirm('Yakin ingin menghapus item ini?')) {
|
||
return;
|
||
}
|
||
|
||
var form = btn.closest('form');
|
||
var listItem = form.closest('li');
|
||
var itemId = form.querySelector('input[name="item_id"]').value;
|
||
|
||
console.log('Menghapus item dengan ID (XMLHttpRequest):', itemId);
|
||
// showNotification('Mencoba hapus dengan metode alternatif...', 'success');
|
||
|
||
// Tampilkan loading effect jika belum
|
||
if (!listItem.classList.contains('opacity-50')) {
|
||
listItem.classList.add('opacity-50');
|
||
}
|
||
|
||
// Buat XMLHttpRequest
|
||
var xhr = new XMLHttpRequest();
|
||
xhr.open('POST', '{{ route("keranjang.hapus.direct") }}', true);
|
||
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
|
||
xhr.setRequestHeader('X-CSRF-TOKEN', '{{ csrf_token() }}');
|
||
xhr.setRequestHeader('Accept', 'application/json');
|
||
|
||
// Format sebagai form data (bukan JSON) untuk kompatibilitas
|
||
var formData = new FormData();
|
||
formData.append('id', itemId);
|
||
formData.append('_ts', new Date().getTime());
|
||
|
||
xhr.onreadystatechange = function() {
|
||
if (xhr.readyState === 4) {
|
||
console.log('Status XHR:', xhr.status);
|
||
console.log('Response XHR:', xhr.responseText);
|
||
|
||
var success = false;
|
||
var message = 'Terjadi kesalahan';
|
||
|
||
try {
|
||
if (xhr.responseText) {
|
||
var response = JSON.parse(xhr.responseText);
|
||
success = response.success;
|
||
message = response.message || message;
|
||
}
|
||
} catch (e) {
|
||
console.error('Error parsing response:', e);
|
||
}
|
||
|
||
if (xhr.status === 200 && success) {
|
||
// Animasi visual sebelum remove
|
||
listItem.style.height = listItem.offsetHeight + 'px';
|
||
setTimeout(function() {
|
||
listItem.style.height = '0';
|
||
listItem.style.opacity = '0';
|
||
listItem.style.padding = '0';
|
||
listItem.style.margin = '0';
|
||
listItem.style.overflow = 'hidden';
|
||
|
||
// Hapus elemen setelah animasi
|
||
setTimeout(function() {
|
||
listItem.remove();
|
||
updateTotal(); // Update subtotal setelah hapus
|
||
|
||
// Periksa apakah keranjang kosong
|
||
var items = document.querySelectorAll('.item-checkbox');
|
||
if (items.length === 0) {
|
||
setTimeout(function() {
|
||
window.location.reload();
|
||
}, 1000);
|
||
}
|
||
|
||
// showNotification('Item berhasil dihapus', 'success');
|
||
}, 300);
|
||
}, 10);
|
||
} else {
|
||
// Kembalikan tampilan item ke normal
|
||
listItem.classList.remove('opacity-50');
|
||
// showNotification(message || 'Gagal menghapus item', 'error');
|
||
}
|
||
}
|
||
};
|
||
|
||
xhr.onerror = function() {
|
||
console.error('Network Error');
|
||
listItem.classList.remove('opacity-50');
|
||
// showNotification('Gagal terhubung ke server', 'error');
|
||
};
|
||
|
||
// Kirim request
|
||
xhr.send(formData);
|
||
}
|
||
|
||
// Fungsi untuk menampilkan notifikasi
|
||
function showNotification(message, type) {
|
||
var notif = document.createElement('div');
|
||
notif.className = type === 'success' ? 'bg-green-500 text-white' : 'bg-red-500 text-white';
|
||
notif.style.padding = '10px 15px';
|
||
notif.style.borderRadius = '4px';
|
||
notif.style.position = 'fixed';
|
||
notif.style.bottom = '20px';
|
||
notif.style.right = '20px';
|
||
notif.style.zIndex = '9999';
|
||
notif.style.boxShadow = '0 2px 4px rgba(0,0,0,0.2)';
|
||
notif.style.transition = 'all 0.3s ease';
|
||
notif.style.opacity = '0';
|
||
notif.style.transform = 'translateY(10px)';
|
||
notif.textContent = message;
|
||
|
||
document.body.appendChild(notif);
|
||
|
||
// Tampilkan dengan animasi
|
||
setTimeout(function() {
|
||
notif.style.opacity = '1';
|
||
notif.style.transform = 'translateY(0)';
|
||
}, 10);
|
||
|
||
// Hilangkan setelah beberapa detik
|
||
setTimeout(function() {
|
||
notif.style.opacity = '0';
|
||
notif.style.transform = 'translateY(10px)';
|
||
|
||
setTimeout(function() {
|
||
notif.remove();
|
||
}, 300);
|
||
}, 3000);
|
||
}
|
||
|
||
// Fungsi untuk submit form hapus terpilih via iframe
|
||
function submitDeleteSelected() {
|
||
var form = document.getElementById('delete-form');
|
||
var selectedCount = document.getElementById('delete-count').textContent;
|
||
|
||
// Cek/buat iframe untuk delete-selected
|
||
var iframe = document.getElementById('hidden-delete-selected-frame');
|
||
if (!iframe) {
|
||
iframe = document.createElement('iframe');
|
||
iframe.setAttribute('id', 'hidden-delete-selected-frame');
|
||
iframe.setAttribute('name', 'hidden-delete-selected-frame');
|
||
iframe.style.display = 'none';
|
||
document.body.appendChild(iframe);
|
||
|
||
// Tambahkan event listener untuk respons iframe
|
||
iframe.onload = function() {
|
||
// Sembunyikan modal
|
||
document.getElementById('delete-modal').classList.add('hidden');
|
||
|
||
// Refresh halaman setelah hapus berhasil
|
||
// Ini lebih mudah karena kita perlu menghapus banyak item
|
||
window.location.reload();
|
||
};
|
||
}
|
||
|
||
// Set target iframe
|
||
form.target = 'hidden-delete-selected-frame';
|
||
|
||
// Submit form
|
||
form.submit();
|
||
|
||
// Tampilkan pesan
|
||
// showNotification(selectedCount + ' item sedang dihapus...', 'success');
|
||
}
|
||
|
||
// Fungsi untuk debug user yang login
|
||
function checkCurrentUser() {
|
||
console.log('Checking current user...');
|
||
fetch('/debug-user')
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
console.log('Current user data:', data);
|
||
// showNotification('User ID: ' + data.user_id + ', nama: ' + data.user_name, 'success');
|
||
})
|
||
.catch(error => {
|
||
console.error('Error checking user:', error);
|
||
// showNotification('Gagal mengambil data user', 'error');
|
||
});
|
||
}
|
||
|
||
// Verifikasi autentikasi
|
||
function verifyAuth() {
|
||
fetch("{{ route('keranjang.index') }}?verify=1", {
|
||
method: 'GET',
|
||
headers: {
|
||
'X-Requested-With': 'XMLHttpRequest',
|
||
'Accept': 'application/json'
|
||
}
|
||
})
|
||
.then(response => {
|
||
// Jika mendapat 401 atau 403, artinya tidak terotentikasi
|
||
if (response.status === 401 || response.status === 403) {
|
||
// showNotification('Sesi Anda telah berakhir. Silakan login kembali.', 'error');
|
||
setTimeout(() => {
|
||
window.location.href = "{{ route('login') }}";
|
||
}, 2000);
|
||
return null;
|
||
}
|
||
return response.json();
|
||
})
|
||
.catch(error => {
|
||
console.error('Error verifikasi autentikasi:', error);
|
||
});
|
||
}
|
||
|
||
// Fungsi untuk tes metode update PUT
|
||
function testUpdateMethod() {
|
||
var firstForm = document.querySelector('form[id^="form-update-"]');
|
||
if (!firstForm) {
|
||
// showNotification('Tidak ada form update ditemukan', 'error');
|
||
return;
|
||
}
|
||
|
||
var url = firstForm.action;
|
||
var itemId = firstForm.getAttribute('id').replace('form-update-', '');
|
||
var currentValue = parseInt(firstForm.querySelector('input[name="jumlah"]').value);
|
||
|
||
console.log('Testing PUT method for item ID:', itemId);
|
||
// showNotification('Testing PUT method untuk item ID: ' + itemId, 'success');
|
||
|
||
// Clone form data
|
||
var formData = new FormData();
|
||
formData.append('jumlah', currentValue);
|
||
formData.append('_method', 'PUT');
|
||
formData.append('_token', document.querySelector('meta[name="csrf-token"]').getAttribute('content'));
|
||
|
||
// Menggunakan fetch dengan method POST + _method=PUT
|
||
fetch(url, {
|
||
method: 'POST',
|
||
headers: {
|
||
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
|
||
'X-Requested-With': 'XMLHttpRequest',
|
||
'Accept': 'application/json'
|
||
},
|
||
body: formData
|
||
})
|
||
.then(response => {
|
||
console.log('Response status:', response.status);
|
||
console.log('Response headers:', response.headers);
|
||
|
||
if (!response.ok) {
|
||
console.error('Response error:', response.status, response.statusText);
|
||
// showNotification('Error: ' + response.status + ' ' + response.statusText, 'error');
|
||
return null;
|
||
}
|
||
|
||
return response.json().catch(() => response.text());
|
||
})
|
||
.then(data => {
|
||
if (data) {
|
||
console.log('Response data:', data);
|
||
if (typeof data === 'object') {
|
||
// showNotification('Sukses: ' + (data.message || 'OK'), 'success');
|
||
} else {
|
||
// showNotification('Sukses dengan response text', 'success');
|
||
}
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Error:', error);
|
||
// showNotification('Error: ' + error.message, 'error');
|
||
});
|
||
}
|
||
|
||
// Fungsi untuk tes metode update direct
|
||
function testDirectMethod() {
|
||
var firstForm = document.querySelector('form[id^="form-update-"]');
|
||
if (!firstForm) {
|
||
// showNotification('Tidak ada form update ditemukan', 'error');
|
||
return;
|
||
}
|
||
|
||
var itemId = firstForm.getAttribute('id').replace('form-update-', '');
|
||
var currentValue = parseInt(firstForm.querySelector('input[name="jumlah"]').value);
|
||
|
||
console.log('Testing direct update for item ID:', itemId);
|
||
// showNotification('Testing direct update untuk item ID: ' + itemId, 'success');
|
||
|
||
// Gunakan metode direct
|
||
var formData = new FormData();
|
||
formData.append('id', itemId);
|
||
formData.append('jumlah', currentValue);
|
||
formData.append('_token', document.querySelector('meta[name="csrf-token"]').getAttribute('content'));
|
||
|
||
fetch("{{ route('keranjang.update.direct') }}", {
|
||
method: 'POST',
|
||
headers: {
|
||
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
|
||
'X-Requested-With': 'XMLHttpRequest',
|
||
'Accept': 'application/json'
|
||
},
|
||
body: formData
|
||
})
|
||
.then(response => {
|
||
console.log('Response status:', response.status);
|
||
|
||
if (!response.ok) {
|
||
console.error('Response error:', response.status, response.statusText);
|
||
// showNotification('Error: ' + response.status + ' ' + response.statusText, 'error');
|
||
return null;
|
||
}
|
||
|
||
return response.json();
|
||
})
|
||
.then(data => {
|
||
if (data) {
|
||
console.log('Response data:', data);
|
||
// showNotification('Sukses: ' + (data.message || 'OK'), 'success');
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Error:', error);
|
||
// showNotification('Error: ' + error.message, 'error');
|
||
});
|
||
}
|
||
|
||
// Fungsi untuk memverifikasi CSRF token
|
||
function verifyCSRF() {
|
||
var token = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
|
||
console.log('CSRF token current value:', token);
|
||
|
||
// Buat request sederhana ke server untuk cek token
|
||
fetch("{{ route('keranjang.index') }}?verify=1", {
|
||
method: 'POST',
|
||
headers: {
|
||
'X-CSRF-TOKEN': token,
|
||
'Content-Type': 'application/json',
|
||
'Accept': 'application/json'
|
||
},
|
||
body: JSON.stringify({ check: 'csrf-verification' })
|
||
})
|
||
.then(response => {
|
||
console.log('CSRF test response status:', response.status);
|
||
|
||
if (response.status === 419) {
|
||
// CSRF token mismatch
|
||
// showNotification('CSRF token tidak valid!', 'error');
|
||
// Muat ulang halaman untuk mendapatkan token baru
|
||
setTimeout(() => {
|
||
window.location.reload();
|
||
}, 2000);
|
||
return null;
|
||
} else if (response.ok) {
|
||
// showNotification('CSRF token valid: ' + token.substring(0, 8) + '...', 'success');
|
||
return response.json().catch(() => null);
|
||
} else {
|
||
// showNotification('Error: ' + response.status, 'error');
|
||
return null;
|
||
}
|
||
})
|
||
.then(data => {
|
||
if (data) {
|
||
console.log('CSRF test response data:', data);
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('CSRF verification error:', error);
|
||
// showNotification('CSRF error: ' + error.message, 'error');
|
||
});
|
||
}
|
||
|
||
// Sinkronisasi nilai jumlah dari form utama ke form fallback
|
||
function syncFallbackValue(itemId) {
|
||
var mainForm = document.getElementById('form-update-' + itemId);
|
||
var fallbackInput = document.getElementById('fallback-jumlah-' + itemId);
|
||
|
||
if (mainForm && fallbackInput) {
|
||
var jumlahInput = mainForm.querySelector('input[name="jumlah"]');
|
||
if (jumlahInput) {
|
||
fallbackInput.value = jumlahInput.value;
|
||
console.log('Nilai jumlah disinkronkan: ' + jumlahInput.value);
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
// Jalankan saat halaman dimuat
|
||
window.onload = function() {
|
||
// Periksa status autentikasi
|
||
verifyAuth();
|
||
|
||
// Pastikan CSRF token tersedia
|
||
refreshCsrfToken();
|
||
|
||
// Sembunyikan alert setelah 3 detik
|
||
setTimeout(function() {
|
||
var successAlert = document.getElementById('success-alert');
|
||
var errorAlert = document.getElementById('error-alert');
|
||
|
||
if (successAlert) successAlert.style.display = 'none';
|
||
if (errorAlert) errorAlert.style.display = 'none';
|
||
}, 3000);
|
||
|
||
// Verifikasi apakah halaman di-reload
|
||
var wasReloaded = performance.navigation.type === 1;
|
||
if (wasReloaded) {
|
||
// Tunggu sedikit agar UI selesai dimuat
|
||
setTimeout(function() {
|
||
// Ambil data dari server untuk memastikan konsistensi
|
||
verifikasiDataKeranjang(true);
|
||
console.log('Halaman di-reload, memverifikasi data keranjang...');
|
||
}, 500);
|
||
}
|
||
|
||
// Setup event listener untuk pesan dari iframe
|
||
window.addEventListener('message', function(event) {
|
||
if (event.data === "delete_success") {
|
||
console.log('Penghapusan berhasil');
|
||
} else if (event.data === "delete_error") {
|
||
console.error('Penghapusan gagal');
|
||
// showNotification('Gagal menghapus item. Halaman akan dimuat ulang.', 'error');
|
||
setTimeout(function() {
|
||
window.location.reload();
|
||
}, 2000);
|
||
} else if (event.data === "update_success") {
|
||
console.log('Update berhasil');
|
||
} else if (event.data === "update_error") {
|
||
console.error('Update gagal');
|
||
// showNotification('Gagal mengubah jumlah. Halaman akan dimuat ulang.', 'error');
|
||
setTimeout(function() {
|
||
window.location.reload();
|
||
}, 2000);
|
||
}
|
||
});
|
||
|
||
// Inisialisasi total
|
||
updateTotal();
|
||
|
||
// Inisialisasi status tombol tambah/kurang
|
||
initButtonState();
|
||
|
||
// Tambahan: Hapus localStorage setelah halaman keranjang dimuat
|
||
// Ini mencegah data checkout tersimpan setelah kembali ke halaman keranjang
|
||
localStorage.removeItem('selected_keranjang_items');
|
||
|
||
// Verifikasi data keranjang dari server setiap 30 detik
|
||
// untuk memastikan UI konsisten dengan data di database
|
||
setInterval(verifikasiDataKeranjang, 30000);
|
||
};
|
||
|
||
// Fungsi untuk memverifikasi data keranjang dengan server
|
||
function verifikasiDataKeranjang(isReload) {
|
||
// Jika ada form yang sedang diproses, tunda verifikasi
|
||
if (document.querySelector('[data-processing="true"]') || document.querySelector('.processing-delete')) {
|
||
console.log('Ada operasi yang sedang berlangsung, menunda verifikasi data keranjang');
|
||
return;
|
||
}
|
||
|
||
// Cek apakah ada perubahan yang belum disimpan
|
||
var isFormChanged = false;
|
||
var forms = document.querySelectorAll('form[id^="form-update-"]');
|
||
forms.forEach(function(form) {
|
||
var input = form.querySelector('input[name="jumlah"]');
|
||
if (input && input.dataset.originalValue && input.value !== input.dataset.originalValue) {
|
||
isFormChanged = true;
|
||
}
|
||
});
|
||
|
||
if (isFormChanged) {
|
||
console.log('Ada perubahan formulir yang belum disimpan, menunda verifikasi data keranjang');
|
||
return;
|
||
}
|
||
|
||
// Gunakan fetch API untuk mendapatkan data terbaru dari server
|
||
fetch("{{ route('keranjang.index') }}?verify=1&_ts=" + new Date().getTime(), {
|
||
method: 'GET',
|
||
headers: {
|
||
'X-Requested-With': 'XMLHttpRequest',
|
||
'Accept': 'application/json'
|
||
}
|
||
})
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
console.log('Data keranjang dari server:', data);
|
||
|
||
// Sinkronkan data dengan UI
|
||
if (data.items && data.items.length > 0) {
|
||
data.items.forEach(function(item) {
|
||
// Cari form untuk item ini
|
||
var form = document.getElementById('form-update-' + item.id);
|
||
if (form) {
|
||
var jumlahInput = form.querySelector('input[name="jumlah"]');
|
||
if (jumlahInput && parseInt(jumlahInput.value) !== item.jumlah) {
|
||
console.log('Sinkronisasi item ' + item.id + ': UI=' + jumlahInput.value + ', Server=' + item.jumlah);
|
||
|
||
// Update nilai input
|
||
jumlahInput.value = item.jumlah;
|
||
jumlahInput.dataset.originalValue = item.jumlah;
|
||
|
||
// Perbarui total harga item
|
||
var listItem = form.closest('li');
|
||
var totalElement = listItem.querySelector('.total-harga');
|
||
if (totalElement) {
|
||
totalElement.innerHTML = 'Rp ' + formatRupiah(item.total_harga);
|
||
}
|
||
|
||
// Perbarui data pada checkbox
|
||
var checkbox = listItem.querySelector('.item-checkbox');
|
||
if (checkbox) {
|
||
checkbox.setAttribute('data-price', item.total_harga);
|
||
checkbox.setAttribute('data-jumlah', item.jumlah);
|
||
|
||
// Update total jika checkbox dicentang
|
||
if (checkbox.checked) {
|
||
updateTotal();
|
||
}
|
||
}
|
||
|
||
// Perbarui status tombol tambah/kurang
|
||
initButtonState();
|
||
|
||
// Tampilkan notifikasi jika ini adalah reload
|
||
if (isReload) {
|
||
// showNotification('Data keranjang telah disinkronkan dengan server', 'success');
|
||
}
|
||
}
|
||
}
|
||
});
|
||
|
||
// Cek apakah ada item yang hilang di server
|
||
var serverItemIds = data.items.map(item => item.id);
|
||
var uiItems = document.querySelectorAll('.item-checkbox');
|
||
|
||
uiItems.forEach(function(checkbox) {
|
||
var itemId = parseInt(checkbox.value);
|
||
if (!serverItemIds.includes(itemId)) {
|
||
// Item tidak ada di server, hapus dari UI
|
||
console.log('Item ' + itemId + ' tidak ditemukan di server, menghapus dari UI');
|
||
var listItem = checkbox.closest('li');
|
||
if (listItem) {
|
||
// Animasi hapus
|
||
listItem.style.height = listItem.offsetHeight + 'px';
|
||
setTimeout(function() {
|
||
listItem.style.height = '0';
|
||
listItem.style.opacity = '0';
|
||
listItem.style.padding = '0';
|
||
listItem.style.margin = '0';
|
||
listItem.style.overflow = 'hidden';
|
||
|
||
// Hapus elemen setelah animasi
|
||
setTimeout(function() {
|
||
listItem.remove();
|
||
updateTotal(); // Update subtotal setelah hapus
|
||
|
||
// Tampilkan notifikasi jika ini adalah reload
|
||
if (isReload) {
|
||
// showNotification('Beberapa item yang tidak valid telah dihapus', 'success');
|
||
}
|
||
|
||
// Periksa apakah keranjang kosong
|
||
var remainingItems = document.querySelectorAll('.item-checkbox');
|
||
if (remainingItems.length === 0) {
|
||
// Reload halaman untuk menampilkan UI keranjang kosong
|
||
window.location.reload();
|
||
}
|
||
}, 300);
|
||
}, 10);
|
||
}
|
||
}
|
||
});
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Error saat memverifikasi data keranjang:', error);
|
||
});
|
||
}
|
||
</script>
|
||
|
||
<div class="container px-4 py-8 mx-auto">
|
||
<div class="flex items-center justify-between mb-6">
|
||
<h1 class="text-2xl font-bold text-gray-800">Keranjang Belanja</h1>
|
||
<div class="flex space-x-2">
|
||
<!-- <button type="button" onclick="checkCurrentUser()" class="px-4 py-2 text-sm font-medium text-white bg-purple-500 rounded-md hover:bg-purple-600">
|
||
<i class="fas fa-user-check mr-2"></i>Cek User
|
||
</button> -->
|
||
<a href="{{ route('barang.index') }}" class="px-4 py-2 text-sm font-medium text-white bg-blue-500 rounded-md hover:bg-blue-600">
|
||
<i class="fas fa-shopping-basket mr-2"></i>Lanjut Belanja
|
||
</a>
|
||
@if(count($items) > 0)
|
||
<form action="{{ route('keranjang.kosongkan') }}" method="POST" class="inline" onsubmit="return confirm('Yakin ingin mengosongkan keranjang?')">
|
||
@csrf
|
||
@method('DELETE')
|
||
<button type="submit" class="px-4 py-2 text-sm font-medium text-white bg-red-500 rounded-md hover:bg-red-600">
|
||
<i class="fas fa-trash mr-2"></i>Kosongkan
|
||
</button>
|
||
</form>
|
||
@endif
|
||
</div>
|
||
</div>
|
||
|
||
@if(session('success'))
|
||
<div class="relative px-4 py-3 mb-4 text-green-700 bg-green-100 border border-green-400 rounded" id="success-alert">
|
||
<span class="block sm:inline">{{ session('success') }}</span>
|
||
<button type="button" class="absolute top-0 right-0 px-4 py-3" onclick="this.parentElement.style.display='none'">
|
||
<i class="fas fa-times"></i>
|
||
</button>
|
||
</div>
|
||
@endif
|
||
|
||
@if(session('error'))
|
||
<div class="relative px-4 py-3 mb-4 text-red-700 bg-red-100 border border-red-400 rounded" id="error-alert">
|
||
<span class="block sm:inline">{{ session('error') }}</span>
|
||
<button type="button" class="absolute top-0 right-0 px-4 py-3" onclick="this.parentElement.style.display='none'">
|
||
<i class="fas fa-times"></i>
|
||
</button>
|
||
</div>
|
||
@endif
|
||
|
||
<!-- <div class="mb-4 p-4 bg-gray-100 rounded-lg">
|
||
<h3 class="text-sm font-semibold text-gray-700 mb-2">Opsi Debug</h3>
|
||
<div class="flex space-x-2">
|
||
<button type="button" onclick="checkCurrentUser()" class="px-2 py-1 text-xs font-medium text-white bg-purple-500 rounded hover:bg-purple-600">
|
||
Cek User
|
||
</button>
|
||
<button type="button" onclick="testUpdateMethod()" class="px-2 py-1 text-xs font-medium text-white bg-blue-500 rounded hover:bg-blue-600">
|
||
Test PUT Method
|
||
</button>
|
||
<button type="button" onclick="testDirectMethod()" class="px-2 py-1 text-xs font-medium text-white bg-green-500 rounded hover:bg-green-600">
|
||
Test Direct Update
|
||
</button>
|
||
<button type="button" onclick="verifyCSRF()" class="px-2 py-1 text-xs font-medium text-white bg-orange-500 rounded hover:bg-orange-600">
|
||
Cek CSRF
|
||
</button>
|
||
</div>
|
||
</div> -->
|
||
|
||
@if(count($items) > 0)
|
||
<div id="checkout-container">
|
||
@csrf
|
||
<div id="selected-items-container"></div>
|
||
<div class="bg-white rounded-xl shadow-md overflow-hidden mb-6">
|
||
<div class="p-6">
|
||
<div class="flex items-center justify-between mb-4">
|
||
<div class="flex items-center">
|
||
<input type="checkbox" id="select-all" onclick="pilihSemua()" class="w-5 h-5 rounded accent-[#2C7A7B] cursor-pointer">
|
||
<label for="select-all" class="ml-2 text-sm font-medium text-gray-700 cursor-pointer">Pilih Semua</label>
|
||
</div>
|
||
<button type="button" id="delete-selected" class="hidden text-sm font-medium text-red-600 hover:text-red-800" onclick="hapusTerpilih()">
|
||
<i class="fas fa-trash mr-1"></i> Hapus Terpilih
|
||
</button>
|
||
</div>
|
||
<div class="flow-root">
|
||
<ul class="-my-6 divide-y divide-gray-200">
|
||
@foreach($items as $item)
|
||
<li class="py-6 flex item-li">
|
||
<div class="flex items-center pr-4">
|
||
<input type="checkbox" name="selected_items[]" value="{{ $item->id }}" data-price="{{ $item->total_harga }}" data-jumlah="{{ $item->jumlah }}" data-harga-satuan="{{ $item->barang->harga }}" onclick="updateTotal()" class="item-checkbox w-5 h-5 rounded accent-[#2C7A7B] cursor-pointer">
|
||
</div>
|
||
<div class="flex-shrink-0 w-24 h-24 rounded-md overflow-hidden border border-gray-200">
|
||
@if($item->barang->gambar)
|
||
<img src="{{ Storage::url($item->barang->gambar) }}" alt="{{ $item->barang->nama_barang }}" class="w-full h-full object-cover">
|
||
@else
|
||
<div class="w-full h-full bg-gray-200 flex items-center justify-center">
|
||
<span class="text-gray-400">No Image</span>
|
||
</div>
|
||
@endif
|
||
</div>
|
||
|
||
<div class="ml-4 flex-1 flex flex-col">
|
||
<div>
|
||
<div class="flex justify-between text-base font-medium text-gray-900">
|
||
<h3>
|
||
<a href="{{ route('barang.show', $item->barang) }}">{{ $item->barang->nama_barang }}</a>
|
||
</h3>
|
||
<p class="ml-4 total-harga">Rp {{ number_format($item->total_harga, 0, ',', '.') }}</p>
|
||
</div>
|
||
<p class="mt-1 text-sm text-gray-500">Harga Satuan: Rp {{ number_format($item->barang->harga, 0, ',', '.') }}</p>
|
||
</div>
|
||
|
||
<div class="flex-1 flex items-end justify-between text-sm">
|
||
<div class="flex items-center">
|
||
<span class="text-gray-500 mr-3">Jumlah:</span>
|
||
<form action="{{ route('keranjang.update', $item) }}" method="POST" class="flex items-center" id="form-update-{{ $item->id }}">
|
||
@csrf
|
||
@method('PUT')
|
||
<input type="hidden" name="_method" value="PUT">
|
||
<button type="button" onclick="kurangJumlah(this, event)" class="p-1 text-gray-500 hover:bg-gray-100 rounded-md transition-opacity">
|
||
<i class="fas fa-minus"></i>
|
||
</button>
|
||
<input type="number" name="jumlah" value="{{ $item->jumlah }}" min="1" max="{{ $item->barang->stok }}"
|
||
class="mx-2 w-16 text-center border-gray-300 rounded-md shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50"
|
||
oninput="updateJumlahInput(this)" onchange="validateAndUpdate(this)">
|
||
<input type="hidden" name="max_stok" value="{{ $item->barang->stok }}">
|
||
<button type="button" onclick="tambahJumlah(this, event)" class="p-1 text-gray-500 hover:bg-gray-100 rounded-md transition-opacity">
|
||
<i class="fas fa-plus"></i>
|
||
</button>
|
||
</form>
|
||
<!-- <div class="ml-2 flex items-center space-x-2">
|
||
<button type="button" onclick="updateJumlahDirect(document.getElementById('form-update-{{ $item->id }}'))"
|
||
class="px-2 text-sm text-blue-600 hover:text-blue-800" title="Update langsung">
|
||
<i class="fas fa-sync-alt"></i>
|
||
</button>
|
||
<form action="{{ route('keranjang.update.fallback') }}" method="POST" style="display:inline;">
|
||
@csrf
|
||
<input type="hidden" name="id" value="{{ $item->id }}">
|
||
<input type="hidden" name="jumlah" id="fallback-jumlah-{{ $item->id }}" value="{{ $item->jumlah }}">
|
||
<button type="submit" class="px-2 text-sm text-green-600 hover:text-green-800"
|
||
title="Update fallback" onclick="syncFallbackValue('{{ $item->id }}')">
|
||
<i class="fas fa-check"></i>
|
||
</button>
|
||
</form>
|
||
</div> -->
|
||
</div>
|
||
|
||
<div class="flex">
|
||
<form action="{{ route('keranjang.hapus.post', $item) }}" method="POST" class="delete-form" novalidate onsubmit="return false;">
|
||
@csrf
|
||
<!-- Tambahkan token csrf secara eksplisit -->
|
||
<input type="hidden" name="_token" value="{{ csrf_token() }}">
|
||
<!-- Tambahkan item ID untuk metode hapus manual -->
|
||
<input type="hidden" name="item_id" value="{{ $item->id }}">
|
||
<div class="flex space-x-2">
|
||
<button type="button" onclick="hapusItem(this)" class="font-medium text-red-600 hover:text-red-500">
|
||
<i class="fas fa-trash mr-1"></i> Hapus
|
||
</button>
|
||
<!-- <button type="button" onclick="hapusItemAlt(this)" class="font-medium text-orange-600 hover:text-orange-500" title="Metode alternatif">
|
||
<i class="fas fa-trash-alt"></i>
|
||
</button>
|
||
<form action="{{ route('keranjang.hapus.fallback') }}" method="POST" style="display:inline;" onsubmit="return confirm('Yakin ingin menghapus dengan metode fallback?');">
|
||
@csrf
|
||
<input type="hidden" name="id" value="{{ $item->id }}">
|
||
<button type="submit" class="font-medium text-green-600 hover:text-green-500" title="Metode fallback">
|
||
<i class="fas fa-check"></i>
|
||
</button>
|
||
</form> -->
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</li>
|
||
@endforeach
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="bg-white rounded-xl shadow-md overflow-hidden">
|
||
<div class="p-6">
|
||
<div class="flex justify-between text-base font-medium text-gray-900 mb-4">
|
||
<p>Subtotal (<span id="selected-count">0</span> item)</p>
|
||
<p>Rp <span id="selected-total">0</span></p>
|
||
</div>
|
||
<div class="mt-6">
|
||
<button href="{{ route('keranjang.checkout-selected') }}" type="button" id="checkout-button" onclick="checkoutItems()" disabled class="w-full flex justify-center items-center px-6 py-3 border border-transparent rounded-md shadow-sm text-base font-medium text-white bg-gray-400 cursor-not-allowed">
|
||
Checkout (<span id="checkout-count">0</span>)
|
||
</button>
|
||
</div>
|
||
<div class="mt-4 flex justify-center text-sm text-gray-500">
|
||
<p>
|
||
<a href="{{ route('barang.index') }}" class="text-[#2C7A7B] font-medium hover:text-[#1C6B6B]">
|
||
<i class="fas fa-arrow-left mr-2"></i>Lanjutkan Belanja
|
||
</a>
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
@else
|
||
<div class="bg-white p-6 rounded-xl shadow-md text-center">
|
||
<div class="flex justify-center mb-4">
|
||
<div class="w-24 h-24 text-gray-300">
|
||
<i class="fas fa-shopping-cart text-6xl"></i>
|
||
</div>
|
||
</div>
|
||
<h3 class="text-lg font-medium text-gray-800 mb-2">Keranjang Anda Kosong</h3>
|
||
<p class="text-gray-500 mb-6">Anda belum menambahkan barang apapun ke keranjang</p>
|
||
<a href="{{ route('barang.index') }}" class="inline-flex items-center px-4 py-2 bg-[#2C7A7B] text-white rounded-lg hover:bg-[#1C6B6B] transition-colors duration-200">
|
||
<i class="fas fa-shopping-bag mr-2"></i>Mulai Belanja
|
||
</a>
|
||
</div>
|
||
@endif
|
||
</div>
|
||
|
||
<!-- Modal Konfirmasi Hapus -->
|
||
<div id="delete-modal" class="hidden fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||
<div class="bg-white rounded-lg p-6 max-w-md w-full mx-4">
|
||
<h3 class="text-lg font-semibold text-gray-900 mb-4">Konfirmasi Hapus</h3>
|
||
<p class="text-gray-600 mb-6">Apakah Anda yakin ingin menghapus <span id="delete-count">0</span> item terpilih?</p>
|
||
<div class="flex justify-end space-x-3">
|
||
<button type="button" onclick="tutupModal()" class="px-4 py-2 bg-gray-300 text-gray-800 rounded-md hover:bg-gray-400">
|
||
Batal
|
||
</button>
|
||
<form action="{{ route('keranjang.hapus-selected') }}" method="POST" id="delete-form">
|
||
@csrf
|
||
@method('DELETE')
|
||
<div id="delete-items-container"></div>
|
||
<button type="button" onclick="submitDeleteSelected()" class="px-4 py-2 bg-red-600 text-white rounded-md hover:bg-red-700">
|
||
Hapus
|
||
</button>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
@endsection
|