Server : Apache/2.4.18 (Ubuntu) System : Linux canvaswebdesign 3.13.0-71-generic #114-Ubuntu SMP Tue Dec 1 02:34:22 UTC 2015 x86_64 User : oppastar ( 1041) PHP Version : 7.0.33-0ubuntu0.16.04.15 Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority, Directory : /var/www/laciasmara.com/public_html/shop/application/views/admin_new/affiliate/ |
Upload File : |
<main class="flex-1 py-4 px-4 bg-purple-50"> <div class="flex items-center mb-4"> <h1 class="text-xl font-bold text-[#333]">Komisi Affiliate</h1> <div class="ml-auto flex items-center gap-2"> <!-- <div class="relative inline-block text-left"> <button type="button" id="btnDownloadData" class="border border-gray-300 text-sm rounded-lg px-4 py-2 bg-white hover:bg-gray-50 gap-4 flex items-center justify-between focus:outline-none focus:ring-1 focus:ring-[#7A4397] hover:bg-[#5a2f73] transition duration-300 ease-in-out text-[#333]"> <i data-feather="download" class="h-4 w-4"></i> <span>Download</span> <i data-feather="chevron-down" id="chevronDownload" class="h-4 w-4 transition-transform transform duration-300"></i> </button> <div id="dropdownMenu" class="hidden opacity-0 scale-95 transform transition-all duration-300 absolute right-0 mt-2 w-48 bg-white border border-gray-200 rounded-lg shadow-lg z-[999]"> <button data-format="csv" class="dropdown-download-item flex items-center gap-2 w-full text-left px-4 py-2 hover:bg-gray-100 transition duration-200"> <i data-feather="file-text" class="h-4 w-4"></i> Download as CSV </button> <button data-format="excel" class="dropdown-download-item flex items-center gap-2 w-full text-left px-4 py-2 hover:bg-gray-100 transition duration-200"> <i data-feather="table" class="h-4 w-4"></i> Download as Excel </button> <button data-format="pdf" class="dropdown-download-item flex items-center gap-2 w-full text-left px-4 py-2 hover:bg-gray-100 transition duration-200"> <i data-feather="file" class="h-4 w-4"></i> Download as PDF </button> </div> </div> --> </div> </div> <?php if ($this->session->flashdata('message')): ?> <div class="alert flex items-center justify-between bg-<?php echo $this->session->flashdata('message_type') === 'success' ? 'green' : 'red'; ?>-100 border-l-4 border-<?php echo $this->session->flashdata('message_type') === 'success' ? 'green' : 'red'; ?>-500 text-<?php echo $this->session->flashdata('message_type') === 'success' ? 'green' : 'red'; ?>-800 px-6 py-4 rounded-lg shadow-lg transition transform duration-300"> <div class="flex items-center"> <!-- Ikon Feather sesuai jenis pesan --> <i data-feather="<?php echo $this->session->flashdata('message_type') === 'success' ? 'check-circle' : 'x-circle'; ?>" class="h-6 w-6 mr-3"></i> <span class="font-semibold"><?php echo $this->session->flashdata('message'); ?></span> </div> <button class="ml-4 text-<?php echo $this->session->flashdata('message_type') === 'success' ? 'green' : 'red'; ?>-500 hover:text-<?php echo $this->session->flashdata('message_type') === 'success' ? 'green' : 'red'; ?>-700 focus:outline-none" onclick="this.parentElement.style.display='none'"> <i data-feather="x" class="h-5 w-5"></i> </button> </div> <script> feather.replace(); </script> <?php endif; ?> <!-- Tabs --> <div class="bg-white rounded-lg shadow-sm p-4"> <!-- Tab Navigation --> <nav class="flex space-x-8" aria-label="Order Tabs"> <button class="tab-btn p-2 text-sm font-medium border-b-2 border-[#7A4397] text-[#7A4397]" data-tab="pending"> Belum Dibayar </button> <button class="tab-btn p-2 text-sm font-medium text-gray-500 hover:text-gray-700" data-tab="paid"> Sudah Dibayar </button> </nav> </div> <div id="affiliate-container" class="mt-4"></div> <div id="loading-more" class="hidden flex justify-center items-center p-4"> <div class="animate-spin rounded-full h-6 w-6 border-b-2 border-green-600"></div> <span class="ml-2 text-gray-600">Memuat data...</span> </div> </main> <!-- Modal Upload Proof --> <div id="upload-proof-modal" class="fixed inset-0 z-[99] bg-black bg-opacity-50 flex justify-center items-center hidden"> <div class="bg-white rounded-lg shadow-lg p-6 w-96"> <h2 class="text-lg font-semibold mb-4">Upload Proof</h2> <input type="hidden" id="affiliate-id"> <input type="hidden" id="account-type"> <input type="hidden" id="account-name"> <input type="hidden" id="account-number"> <input type="hidden" id="commission"> <input type="file" id="proof-file" accept="image/jpeg, image/png" class="mb-4"> <div class="flex justify-end space-x-2"> <button id="cancel-upload" class="px-4 py-2 bg-gray-300 rounded">Cancel</button> <button id="submit-upload" class="px-4 py-2 bg-blue-600 text-white rounded">Upload</button> </div> </div> </div> <div id="proof-modal" class="hidden fixed inset-0 z-[99] bg-gray-900 bg-opacity-50 flex items-center justify-center"> <div class="bg-white p-5 rounded-lg shadow-lg max-w-md"> <h3 class="text-lg font-semibold mb-3">Bukti Pembayaran</h3> <img id="proof-image" src="" alt="Proof Image" class="w-full h-auto rounded-lg shadow-md"> <button id="close-proof" class="mt-3 px-4 py-2 bg-gray-500 text-white rounded hover:bg-gray-700">Tutup</button> </div> </div> <script> let currentPage = 1; let hasMoreData = true; let isFetching = false; let allAffiliates = []; let debounceTimer; let searchDebounceTimer; let currentTab = new URLSearchParams(window.location.search).get('tab') || 'pending'; const tabs = document.querySelectorAll('.tab-btn'); const BASE_URL_IMAGE = "<?= base_url('uploads/product') ?>"; const BASE_URL_PRODUCT = "<?= base_url('product') ?>"; const BASE_URL_FETCH_DATA = "<?= base_url('admin/affiliator/get_commissions') ?>"; const BASE_URL = "<?= base_url('admin/affiliator') ?>"; document.addEventListener("DOMContentLoaded", function() { // Set default tab saat halaman dimuat tabs.forEach(tab => { if (tab.getAttribute('data-tab') === currentTab) { tab.classList.add('text-[#7A4397]', 'border-b-2', 'border-[#7A4397]'); tab.classList.remove('text-gray-500', 'hover:text-gray-700'); updateURLAndFetch(); } else { tab.classList.remove('text-[#7A4397]', 'border-b-2', 'border-[#7A4397]'); tab.classList.add('text-gray-500', 'hover:text-gray-700'); } }); // Handle Tab Clicks tabs.forEach(tab => { tab.addEventListener('click', () => { tabs.forEach(t => { t.classList.remove('text-[#7A4397]', 'border-b-2', 'border-[#7A4397]'); t.classList.add('text-gray-500', 'hover:text-gray-700'); }); tab.classList.add('text-[#7A4397]', 'border-b-2', 'border-[#7A4397]'); tab.classList.remove('text-gray-500', 'hover:text-gray-700'); currentTab = tab.getAttribute('data-tab'); updateURLAndFetch(); }); }); // // Download Data // document.getElementById("btnDownloadData").addEventListener("click", function() { // let dropdown = document.getElementById("dropdownMenu"); // let iconChevron = document.getElementById("chevronDownload"); // if (dropdown.classList.contains("hidden")) { // dropdown.classList.remove("hidden"); // setTimeout(() => { // dropdown.classList.remove("opacity-0", "scale-95"); // }, 10); // iconChevron.classList.add("rotate-180"); // } else { // dropdown.classList.add("opacity-0", "scale-95"); // setTimeout(() => { // dropdown.classList.add("hidden"); // }, 200); // iconChevron.classList.remove("rotate-180"); // } // }); // Handle click opsi // document.querySelectorAll(".dropdown-download-item").forEach(item => { // item.addEventListener("click", function() { // let format = this.getAttribute("data-format"); // if (format === "csv") { // downloadOrdersCSV(allAffiliates); // } else if (format === "excel") { // downloadOrdersExcel(allAffiliates); // } else if (format === "pdf") { // downloadOrdersPDF(allAffiliates); // } // let dropdown = document.getElementById("dropdownMenu"); // dropdown.classList.add("opacity-0", "scale-95"); // setTimeout(() => { // dropdown.classList.add("hidden"); // }, 200); // document.getElementById("chevronDownload").classList.remove("rotate-180"); // }); // }); // // Hide dropdown jika klik di luar // document.addEventListener("click", function(event) { // let dropdown = document.getElementById("dropdownMenu"); // let button = document.getElementById("btnDownloadData"); // if (!button.contains(event.target) && !dropdown.contains(event.target)) { // dropdown.classList.add("opacity-0", "scale-95"); // setTimeout(() => { // dropdown.classList.add("hidden"); // }, 200); // document.getElementById("chevronDownload").classList.remove("rotate-180"); // } // }); // Fetch data customer infinite scroll window.addEventListener('scroll', () => { if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight - 500) { fetchAffiliates(new URLSearchParams(window.location.search), true); } }); feather.replace(); updateURLAndFetch(); }); function updateURLAndFetch(paramName, value, extraParams = null) { clearTimeout(debounceTimer); debounceTimer = setTimeout(() => { const params = new URLSearchParams(window.location.search); params.set('tab', currentTab); // Perbarui atau hapus parameter yang ditentukan if (paramName) { if (value !== null && value !== undefined) { params.set(paramName, value); } else { params.delete(paramName); } } // Reset pagination currentPage = 1; hasMoreData = true; allAffiliates = []; // Update URL const newUrl = `${window.location.pathname}?${params.toString()}`; history.pushState(null, '', newUrl); // Fetch affiliates with new parameters (replace mode) fetchAffiliates(params, false); }, 300); } function fetchAffiliates(params = new URLSearchParams(), append = false) { if (isFetching) return; // Jika tidak dalam mode append dan tidak ada data lagi, reset hasMoreData if (!append) { hasMoreData = true; } else if (!hasMoreData) { return; } isFetching = true; const baseUrl = BASE_URL_FETCH_DATA; params.set('page', currentPage); params.set('limit', 10); const url = `${baseUrl}?${params.toString()}`; const progressBar = document.getElementById('progressBar'); const container = document.getElementById('affiliate-container'); // Hapus konten sebelum fetch (kecuali jika append = true) if (!append) { container.innerHTML = ` <div class="flex flex-col items-center justify-center p-8"> <div class="animate-spin rounded-full h-12 w-12 border-b-2 border-purple-700 mb-4"></div> <p class="text-gray-600 text-center">Sebentar lagi ngitung kliknya, sabar ya...</p> </div> `; } progressBar.classList.remove('hidden'); fetch(url) .then(res => res.json()) .then(data => { progressBar.classList.add('hidden'); if (!append) { container.innerHTML = ''; // Hapus loading setelah data diterima } if (data.length === 0) { hasMoreData = false; if (!append) { container.innerHTML = '<div class="flex justify-center items-center p-8 text-gray-500">Tidak ada affiliate yang ditemukan</div>'; } } else { if (!append) { allAffiliates = data; } else { allAffiliates = [...allAffiliates, ...data]; } renderAffiliates(data, append); currentPage++; } console.log(allAffiliates); isFetching = false; }) .catch(error => { progressBar.classList.add('hidden'); console.error('Error fetching affiliates:', error); isFetching = false; // Tampilkan pesan error container.innerHTML = '<div class="flex justify-center items-center p-8 text-red-500">Terjadi kesalahan saat mengambil data. Silakan coba lagi.</div>'; }); } function formatNumber(number) { return new Intl.NumberFormat('id-ID').format(number); } function renderAffiliates(affiliates, append = false) { const container = document.getElementById('affiliate-container'); const loadingMore = document.getElementById('loading-more'); const currentTab = new URLSearchParams(window.location.search).get('tab') || 'pending'; if (!append) { container.innerHTML = ''; // Clear existing content } if (!affiliates || affiliates.length === 0) { if (!append) { container.innerHTML = '<div class="flex justify-center items-center p-8 text-gray-500">Tidak ada pelanggan yang ditemukan</div>'; } loadingMore.classList.add('hidden'); return; } // Jika tidak append, buat struktur tabel baru if (!append) { const tableWrapper = document.createElement('div'); tableWrapper.className = 'overflow-x-auto rounded-lg shadow'; tableWrapper.innerHTML = ` <table class="min-w-full divide-y divide-gray-200"> <thead class="bg-gray-50"> <tr> <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Affiliate</th> <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Sosial Media</th> <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Bank</th> <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Nama Rekening</th> <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Nomor Rekening</th> <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Komisi</th> <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Aksi</th> </tr> </thead> <tbody id="affiliates-table-body" class="bg-white divide-y divide-gray-200"> </tbody> </table> `; container.appendChild(tableWrapper); } const tableBody = document.getElementById('affiliates-table-body') || container.querySelector('tbody'); affiliates.forEach(affiliate => { const row = document.createElement('tr'); row.className = 'hover:bg-gray-50 transition-colors'; const commissionValue = currentTab === 'paid' ? affiliate.paid_commission_formatted : affiliate.pending_commission_formatted; const actionButton = currentTab === 'paid' ? `<button data-id="${affiliate.id_daftar}" data-proof="${affiliate.proof}" class="view-proof-btn inline-flex items-center px-3 py-1.5 border border-gray-300 rounded-md text-xs text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[#7A4397]"> <i data-feather="eye" class="w-4 h-4 mr-1"></i> View Proof </button>` : `<button data-id="${affiliate.id_daftar}" data-commission="${affiliate.pending_commission}" data-name="${affiliate.account_name}" data-type="${affiliate.account_type}" data-number="${affiliate.account_number}" class="upload-proof-btn inline-flex items-center px-3 py-1.5 border border-transparent rounded-md text-xs text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"> <i data-feather="upload" class="w-4 h-4 mr-1"></i> Upload Proof </button>`; row.innerHTML = ` <td class="px-4 py-3"> <div class="flex items-center"> <div class="flex-shrink-0 h-10 w-10 rounded-full bg-gray-200 flex items-center justify-center"> <span class="text-gray-600 font-medium">${affiliate.nama.charAt(0).toUpperCase()}</span> </div> <div class="ml-4"> <div class="text-sm font-medium text-[#333]">${affiliate.nama}</div> <div class="text-sm text-gray-500">${affiliate.email}</div> <div class="text-sm text-gray-500">${affiliate.no_telpon}</div> </div> </div> </td> <td class="px-4 py-3 max-w-xs truncate"> <a href="${affiliate.sosial_media}" target="_blank" class="text-sm font-medium text-purple-500 hover:underline"> ${affiliate.sosial_media} </a> </td> <td class="px-4 py-3"> <div class="text-sm font-medium text-[#333]">${affiliate.account_type}</div> </td> <td class="px-4 py-3"> <div class="text-sm font-medium text-[#333]">${affiliate.account_name}</div> </td> <td class="px-4 py-3"> <div class="text-sm font-medium text-[#333]">${affiliate.account_number}</div> </td> <td class="px-4 py-3"> <div class="text-sm font-medium text-[#333]">${commissionValue}</div> </td> <td class="px-4 py-3 text-right text-sm font-medium space-y-2"> <div class="flex flex-col space-y-2"> ${actionButton} </div> </td> `; tableBody.appendChild(row); }); loadingMore.classList.add('hidden'); // Inisialisasi Feather Icons feather.replace(); document.querySelectorAll('.upload-proof-btn').forEach(button => { button.addEventListener('click', function() { const affiliateId = this.dataset.id; const accountType = this.dataset.type; const accountName = this.dataset.name; const accountNumber = this.dataset.number; const commission = this.dataset.commission; document.getElementById('affiliate-id').value = affiliateId; document.getElementById('account-type').value = accountType; document.getElementById('account-name').value = accountName; document.getElementById('account-number').value = accountNumber; document.getElementById('commission').value = commission; document.getElementById('upload-proof-modal').classList.remove('hidden'); }); }); // Event listener untuk tombol Cancel di modal document.getElementById('cancel-upload').addEventListener('click', function() { document.getElementById('upload-proof-modal').classList.add('hidden'); }); // Event listener untuk tombol Upload document.getElementById('submit-upload').addEventListener('click', async function() { const affiliateId = document.getElementById('affiliate-id').value; const accountType = document.getElementById('account-type').value; const accountName = document.getElementById('account-name').value; const accountNumber = document.getElementById('account-number').value; const commission = document.getElementById('commission').value; const CSRF_TOKEN = "<?= $this->security->get_csrf_token_name(); ?>"; const CSRF_HASH = "<?= $this->security->get_csrf_hash(); ?>"; const fileInput = document.getElementById('proof-file'); const file = fileInput.files[0]; if (!file) { alert('Silakan pilih file gambar untuk diupload.'); return; } if (!['image/jpeg', 'image/png'].includes(file.type)) { alert('Hanya diperbolehkan mengupload file JPG atau PNG.'); return; } const formData = new FormData(); formData.append('proof', file); formData.append('account_name', accountName); // Ganti dengan data asli formData.append('account_number', accountNumber); // Ganti dengan data asli formData.append('account_type', accountType); // Ganti dengan data asli formData.append('commission', commission); // Ganti dengan nilai asli formData.append('status', 'Completed'); // Default status formData.append(CSRF_TOKEN, CSRF_HASH); fetch(`<?= base_url('admin/affiliator/upload_proof/') ?>${affiliateId}`, { method: 'POST', headers: { 'X-Requested-With': 'XMLHttpRequest' }, body: formData }) .then(response => response.json()) .then(data => { if (data.success) { notyf.success('Proof berhasil diupload'); setTimeout(() => { window.location.reload(); }, 2000); } else { notyf.error('Gagal upload: ' + (data.message || 'Terjadi kesalahan')); } }) .catch(error => { console.error('Error uploading proof:', error); notyf.error('Terjadi kesalahan saat mengupload proof'); }); }); // Event listener untuk "View Proof" document.querySelectorAll('.view-proof-btn').forEach(button => { button.addEventListener('click', function() { const proofUrl = this.dataset.proof; if (!proofUrl) { notyf.error('Bukti pembayaran tidak tersedia.'); return; } // Tampilkan modal document.getElementById('proof-image').src = proofUrl; document.getElementById('proof-modal').classList.remove('hidden'); }); }); document.getElementById('close-proof').addEventListener('click', function() { document.getElementById('proof-modal').classList.add('hidden'); }); } </script>