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/orders/ |
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]">Daftar Pesanan</h1> <div class="ml-auto flex items-center gap-2"> <button type="button" id="addOrder" 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] hover:bg-[#5a2f73] transition duration-300 ease-in-out text-[#333]"> <i data-feather="plus" class="h-4 w-4"></i> <span>Tambah Pesanan</span> </button> <button type="button" id="printLabel" 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] hover:bg-[#5a2f73] transition duration-300 ease-in-out text-[#333]"> <i data-feather="printer" class="h-4 w-4"></i> <span>Cetak Label</span> </button> <div class="relative inline-block text-left"> <!-- Button Download --> <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> <!-- Dropdown --> <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> <!-- Date Filter Dropdown --> <div class="relative" id="dateFilterDropdown"> <button class="border border-gray-300 text-sm rounded-lg px-4 py-2 bg-white hover:bg-gray-50 flex items-center justify-between w-[350px] focus:outline-none focus:ring-1 focus:ring-[#7A4397]"> <div class="flex items-center"> <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2 text-[#333]" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" /> </svg> <span id="dateFilterText">Pilih Tanggal</span> </div> <i data-feather="chevron-down" class="w-4 h-4 text-gray-700"></i> </button> <div class="hidden absolute right-0 top-full mt-2 bg-white border border-gray-200 rounded-lg shadow-lg z-50 w-[650px]" id="dateFilterMenu"> <div class="p-4"> <!-- Two-column layout with fixed left column --> <div class="flex"> <!-- Left Column - Fixed options --> <div class="w-1/2 border-r border-gray-200 pr-2"> <h3 class="text-gray-700 font-medium mb-2">Rentang waktu</h3> <div class="flex flex-col"> <!-- Hari ini --> <div class="py-3 hover:bg-gray-50 cursor-pointer date-option" data-option="today"> <div class="text-gray-800 pl-2">Hari ini</div> </div> <!-- Kemarin --> <div class="py-3 hover:bg-gray-50 cursor-pointer date-option" data-option="yesterday"> <div class="text-gray-800 pl-2">Kemarin</div> </div> <!-- 7 hari terakhir --> <div class="py-3 hover:bg-gray-50 cursor-pointer date-option" data-option="last7days"> <div class="text-gray-800 pl-2">7 hari terakhir</div> </div> <!-- 30 hari terakhir --> <div class="py-3 hover:bg-gray-50 cursor-pointer date-option" data-option="last30days"> <div class="text-gray-800 pl-2">30 hari terakhir</div> </div> <!-- Bulan ini --> <div class="py-3 hover:bg-gray-50 cursor-pointer date-option" data-option="thisMonth"> <div class="text-gray-800 pl-2">Bulan ini</div> </div> <!-- Tahun ini --> <div class="py-3 hover:bg-gray-50 cursor-pointer date-option" data-option="thisYear"> <div class="text-gray-800 pl-2">Tahun ini</div> </div> <!-- Separator --> <div class="my-2 border-t border-gray-200"></div> <!-- Pilih Tanggal with active indicator --> <div id="customDateOption" class="py-3 hover:bg-gray-50 cursor-pointer date-option relative" data-option="customDate"> <div class="text-gray-800 pl-2 flex items-center"> <span>Pilih Tanggal</span> <div id="customDateIndicator" class="hidden absolute left-0 top-0 bottom-0 w-1 bg-green-500"></div> </div> </div> </div> </div> <!-- Right Column - Dynamic content --> <div class="w-1/2 pl-2"> <!-- Date display for predefined options --> <div id="predefinedDateInfo"> <h3 class="text-gray-700 font-medium mb-2" id="currentMonthTitle">Februari 2025</h3> <div id="dateDisplays"> <div class="py-3" id="todayDateDisplay"> <div class="text-gray-600" id="todayDate">28 Feb 2025 (00:00 - 11:00)</div> </div> <div class="py-3 hidden" id="yesterdayDateDisplay"> <div class="text-gray-600" id="yesterdayDate">27 Feb 2025</div> </div> <div class="py-3 hidden" id="last7daysDateDisplay"> <div class="text-gray-600" id="last7daysDate">21 Feb - 27 Feb 2025</div> </div> <div class="py-3 hidden" id="last30daysDateDisplay"> <div class="text-gray-600" id="last30daysDate">29 Jan - 27 Feb 2025</div> </div> <div class="py-3 hidden" id="thisMonthDateDisplay"> <div class="text-gray-600" id="thisMonthDate">01 Feb - 28 Feb 2025</div> </div> <div class="py-3 hidden" id="perDayDateDisplay"> <div class="text-gray-600">Filter per hari</div> </div> <div class="py-3 hidden" id="perWeekDateDisplay"> <div class="text-gray-600">Filter per minggu</div> </div> <div class="py-3 hidden" id="perMonthDateDisplay"> <div class="text-gray-600">Filter per bulan</div> </div> </div> </div> <!-- Calendar for custom date --> <div id="calendarContainer" class="hidden"> <div class="text-center mb-2"> <div id="customDateMessage" class="text-gray-500 text-sm">Kamu belum pilih tanggal</div> </div> <div id="flatpickrContainer"></div> </div> </div> </div> </div> </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 mb-4"> <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="all"> Semua Pesanan </button> <button class="tab-btn p-2 text-sm font-medium text-gray-500 hover:text-gray-700" data-tab="pending"> Perlu Diproses </button> <button class="tab-btn p-2 text-sm font-medium text-gray-500 hover:text-gray-700" data-tab="processing"> Lagi Diproses </button> <button class="tab-btn p-2 text-sm font-medium text-gray-500 hover:text-gray-700" data-tab="shipping"> Lagi Dikirim </button> <button class="tab-btn p-2 text-sm font-medium text-gray-500 hover:text-gray-700" data-tab="done"> Pesanan Selesai </button> <button class="tab-btn p-2 text-sm font-medium text-gray-500 hover:text-gray-700" data-tab="cancelled"> Dibatalin </button> </nav> </div> <!-- Search and Filters --> <div class="mt-4 flex flex-col gap-4 sm:flex-row sm:flex-wrap text-sm"> <!-- Filter Dropdowns --> <div class="flex flex-wrap gap-2"> <!-- Filter Dropdown --> <div class="relative" id="filterDropdown"> <button class="border border-gray-300 text-sm rounded-lg px-4 py-2 bg-white hover:bg-gray-50 flex items-center justify-between w-[200px] focus:outline-none focus:ring-1 focus:ring-[#7A4397]"> <span id="filterText">Filter</span> <i data-feather="chevron-down" class="w-4 h-4 text-gray-700"></i> </button> <div class="hidden absolute left-0 top-full mt-1 bg-white border border-gray-200 rounded-lg shadow-lg z-50 min-w-[200px]" id="filterMenu"> <!-- Main Menu Items --> <div class="py-1"> <!-- Metode Pembayaran --> <div class="px-4 py-2 text-sm hover:bg-gray-100 cursor-pointer flex justify-between items-center group relative"> <span>Tipe Pembayaran</span> <i data-feather="chevron-right" class="w-4 h-4 text-gray-700"></i> <div class="hidden group-hover:block absolute left-full top-0 bg-white border border-gray-200 rounded-lg shadow-lg min-w-[200px] -mt-1"> <div class="py-1"> <label class="flex items-center px-4 py-2 hover:bg-gray-100 cursor-pointer"> <input type="checkbox" class="mr-2 filter-checkbox" data-filter="payment" data-param="isBCATransfer" data-value="true"> <span>Transfer BCA</span> </label> <label class="flex items-center px-4 py-2 hover:bg-gray-100 cursor-pointer"> <input type="checkbox" class="mr-2 filter-checkbox" data-filter="payment" data-param="isMandiriTransfer" data-value="true"> <span>Transfer MANDIRI</span> </label> <label class="flex items-center px-4 py-2 hover:bg-gray-100 cursor-pointer"> <input type="checkbox" class="mr-2 filter-checkbox" data-filter="payment" data-param="isPaypal" data-value="true"> <span>PayPal</span> </label> <label class="flex items-center px-4 py-2 hover:bg-gray-100 cursor-pointer"> <input type="checkbox" class="mr-2 filter-checkbox" data-filter="payment" data-param="isDoku" data-value="true"> <span>DOKU</span> </label> </div> </div> </div> <!-- Metode Pengiriman --> <div class="px-4 py-2 text-sm hover:bg-gray-100 cursor-pointer flex justify-between items-center group relative"> <span>Metode Pengiriman</span> <i data-feather="chevron-right" class="w-4 h-4 text-gray-700"></i> <div class="hidden group-hover:block absolute left-full top-0 bg-white border border-gray-200 rounded-lg shadow-lg min-w-[200px] -mt-1"> <div class="py-1"> <label class="flex items-center px-4 py-2 hover:bg-gray-100 cursor-pointer"> <input type="checkbox" class="mr-2 filter-checkbox" data-filter="shipping" data-param="isRegularShipping" data-value="true"> <span>Regular</span> </label> <label class="flex items-center px-4 py-2 hover:bg-gray-100 cursor-pointer"> <input type="checkbox" class="mr-2 filter-checkbox" data-filter="shipping" data-param="isTwoHourShipping" data-value="true"> <span>2 Hour Delivery</span> </label> <label class="flex items-center px-4 py-2 hover:bg-gray-100 cursor-pointer"> <input type="checkbox" class="mr-2 filter-checkbox" data-filter="shipping" data-param="isOneDayShipping" data-value="true"> <span>One Day Service</span> </label> <label class="flex items-center px-4 py-2 hover:bg-gray-100 cursor-pointer"> <input type="checkbox" class="mr-2 filter-checkbox" data-filter="shipping" data-param="isNextDayShipping" data-value="true"> <span>Next Day Service</span> </label> </div> </div> </div> <!-- Asuransi --> <div class="px-4 py-2 text-sm hover:bg-gray-100 cursor-pointer flex justify-between items-center group relative"> <span>Asuransi Pengiriman</span> <i data-feather="chevron-right" class="w-4 h-4 text-gray-700"></i> <div class="hidden group-hover:block absolute left-full top-0 bg-white border border-gray-200 rounded-lg shadow-lg min-w-[200px] -mt-1"> <div class="py-1"> <label class="flex items-center px-4 py-2 hover:bg-gray-100 cursor-pointer"> <input type="checkbox" class="mr-2 filter-checkbox" data-filter="insurance" data-param="usingInsurance" data-value="true"> <span>Pakai Asuransi</span> </label> <label class="flex items-center px-4 py-2 hover:bg-gray-100 cursor-pointer"> <input type="checkbox" class="mr-2 filter-checkbox" data-filter="insurance" data-param="notUsingInsurance" data-value="true"> <span>Ga Pakai Asuransi</span> </label> </div> </div> </div> <!-- Shipping Fee --> <div class="px-4 py-2 text-sm hover:bg-gray-100 cursor-pointer flex justify-between items-center group relative"> <span>Ongkos Kirim</span> <i data-feather="chevron-right" class="w-4 h-4 text-gray-700"></i> <div class="hidden group-hover:block absolute left-full top-0 bg-white border border-gray-200 rounded-lg shadow-lg min-w-[200px] -mt-1"> <div class="py-1"> <label class="flex items-center px-4 py-2 hover:bg-gray-100 cursor-pointer"> <input type="checkbox" class="mr-2 filter-checkbox" data-filter="shipping_fee" data-param="usingShippingFee" data-value="true"> <span>Pakai Ongkir</span> </label> <label class="flex items-center px-4 py-2 hover:bg-gray-100 cursor-pointer"> <input type="checkbox" class="mr-2 filter-checkbox" data-filter="shipping_fee" data-param="notUsingShippingFee" data-value="true"> <span>Ga Pakai Ongkir</span> </label> </div> </div> </div> </div> </div> </div> <div class="flex flex-wrap gap-2 text-sm"> <!-- Sort Select --> <div class="relative w-[150px]"> <select id="sortFilter" name="sort" class="w-full appearance-none border border-gray-300 rounded-lg px-4 py-2 bg-white pr-10 cursor-pointer focus:outline-none focus:ring-1 focus:ring-[#7A4397] truncate"> <option value="" class="truncate">Urutkan</option> <option value="paling_baru">Paling Baru</option> <option value="paling_lama">Paling Lama</option> </select> <div class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-700"> <i data-feather="chevron-down" class="w-4 h-4"></i> </div> </div> </div> </div> <button class="marketing-button border rounded-lg px-4 py-2 flex items-center justify-center transition-all duration-200 ease-in-out focus:outline-none" data-filter="department" data-param="department" data-value="marketing"> <span class="flex items-center"> Marketing </span> </button> <button class="tokopedia-button border rounded-lg px-4 py-2 flex items-center justify-center transition-all duration-200 ease-in-out focus:outline-none" data-filter="marketplace" data-param="marketplace" data-value="tokopedia"> <span class="flex items-center"> Tokopedia </span> </button> <button class="website-button border rounded-lg px-4 py-2 flex items-center justify-center transition-all duration-200 ease-in-out focus:outline-none" data-filter="marketplace" data-param="marketplace" data-value="website"> <span class="flex items-center"> Website </span> </button> <!-- Search --> <div class="w-full sm:flex-1 sm:min-w-[300px]"> <div class="relative"> <input id="searchInput" type="text" placeholder="Cari berdasarkan Order ID, Nama Produk, Invoice Tokopedia, Nama Customer, Email, atau Voucher" class="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-1 focus:ring-[#7A4397]"> <div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none"> <svg class="h-5 w-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" /> </svg> </div> </div> </div> </div> <!-- Selected Filters Display --> <div id="selectedFilters" class="mt-4 flex flex-wrap gap-2 items-center"> <span id="resetFilters" class="text-[#7A4397] text-sm mr-2 font-semibold cursor-pointer hidden">Reset Filter</span> </div> <div id="orders-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 --> <div id="changeStatusModal" class="fixed inset-0 bg-gray-800 z-[999] bg-opacity-50 flex justify-center items-center hidden"> <div class="bg-white rounded-lg shadow-xl p-6 w-96 max-w-md"> <div class="flex justify-between items-center mb-5 border-b pb-3"> <h2 class="text-xl font-semibold text-gray-800">Ubah Status Pesanan</h2> <button id="closeModal" class="text-gray-500 hover:text-gray-700 focus:outline-none"> <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" /> </svg> </button> </div> <p class="text-gray-600 mb-4 text-sm">Pilih status baru untuk pesanan ini. Status tertentu mungkin tidak tersedia berdasarkan status saat ini.</p> <!-- Status Options --> <div id="statusOptions" class="space-y-3 mb-6"> <label class="flex items-center space-x-2 cursor-pointer group"> <input type="radio" name="status" value="0" class="hidden peer status-radio"> <div class="w-full py-3 px-4 rounded-lg border border-gray-300 text-gray-700 peer-checked:border-[#7A4397] peer-checked:bg-[#7A4397] peer-checked:text-white transition-all hover:border-[#7A4397] hover:bg-purple-50 flex items-center space-x-2"> <div class="h-3 w-3 rounded-full bg-gray-400 peer-checked:bg-white"></div> <span class="font-medium">Pending</span> <span class="text-xs ml-auto text-gray-500 peer-checked:text-white">Menunggu konfirmasi</span> </div> </label> <label class="flex items-center space-x-2 cursor-pointer group"> <input type="radio" name="status" value="1" class="hidden peer status-radio"> <div class="w-full py-3 px-4 rounded-lg border border-gray-300 text-gray-700 peer-checked:border-orange-500 peer-checked:bg-orange-500 peer-checked:text-white transition-all hover:border-orange-500 hover:bg-orange-50 flex items-center space-x-2"> <div class="h-3 w-3 rounded-full bg-gray-400 peer-checked:bg-white"></div> <span class="font-medium">Belum Dibayar</span> <span class="text-xs ml-auto text-gray-500 peer-checked:text-white">Menunggu pembayaran</span> </div> </label> <label class="flex items-center space-x-2 cursor-pointer group"> <input type="radio" name="status" value="2" class="hidden peer status-radio"> <div class="w-full py-3 px-4 rounded-lg border border-gray-300 text-gray-700 peer-checked:border-red-500 peer-checked:bg-red-500 peer-checked:text-white transition-all hover:border-red-500 hover:bg-red-50 flex items-center space-x-2"> <div class="h-3 w-3 rounded-full bg-gray-400 peer-checked:bg-white"></div> <span class="font-medium">Dibatalkan</span> <span class="text-xs ml-auto text-gray-500 peer-checked:text-white">Pesanan dibatalkan</span> </div> </label> <label class="flex items-center space-x-2 cursor-pointer group"> <input type="radio" name="status" value="3" class="hidden peer status-radio"> <div class="w-full py-3 px-4 rounded-lg border border-gray-300 text-gray-700 peer-checked:border-green-500 peer-checked:bg-green-500 peer-checked:text-white transition-all hover:border-green-500 hover:bg-green-50 flex items-center space-x-2"> <div class="h-3 w-3 rounded-full bg-gray-400 peer-checked:bg-white"></div> <span class="font-medium">Sudah Dibayar</span> <span class="text-xs ml-auto text-gray-500 peer-checked:text-white">Pembayaran diterima</span> </div> </label> <label class="flex items-center space-x-2 cursor-pointer group"> <input type="radio" name="status" value="4" class="hidden peer status-radio"> <div class="w-full py-3 px-4 rounded-lg border border-gray-300 text-gray-700 peer-checked:border-blue-500 peer-checked:bg-blue-500 peer-checked:text-white transition-all hover:border-blue-500 hover:bg-blue-50 flex items-center space-x-2"> <div class="h-3 w-3 rounded-full bg-gray-400 peer-checked:bg-white"></div> <span class="font-medium">Diproses</span> <span class="text-xs ml-auto text-gray-500 peer-checked:text-white">Sedang disiapkan</span> </div> </label> <label class="flex items-center space-x-2 cursor-pointer group"> <input type="radio" name="status" value="5" class="hidden peer status-radio"> <div class="w-full py-3 px-4 rounded-lg border border-gray-300 text-gray-700 peer-checked:border-purple-500 peer-checked:bg-purple-500 peer-checked:text-white transition-all hover:border-purple-500 hover:bg-purple-50 flex items-center space-x-2"> <div class="h-3 w-3 rounded-full bg-gray-400 peer-checked:bg-white"></div> <span class="font-medium">Selesai</span> <span class="text-xs ml-auto text-gray-500 peer-checked:text-white">Pesanan telah selesai</span> </div> </label> </div> <!-- Action Buttons --> <div class="flex justify-end mt-6 border-t pt-4"> <button id="cancelModal" class="px-4 py-2 text-gray-700 bg-gray-100 rounded-md hover:bg-gray-200 transition-colors font-medium focus:outline-none focus:ring-2 focus:ring-gray-300"> Batal </button> <button id="confirmStatus" class="px-6 py-2 bg-[#7A4397] text-white rounded-md ml-3 hover:bg-[#693a84] transition-colors font-medium focus:outline-none focus:ring-2 focus:ring-[#7A4397]"> Simpan Perubahan </button> </div> </div> </div> <!-- Modal Konfirmasi --> <div id="modalResi" class="fixed inset-0 z-[999] flex items-center justify-center bg-black bg-opacity-50 hidden"> <div class="bg-white rounded-lg p-6 w-80 shadow-lg"> <h2 class="text-lg font-semibold">Konfirmasi</h2> <p class="text-sm text-gray-600 my-2">Apakah Anda yakin ingin mengupdate nomor resi?</p> <div class="flex justify-end gap-2 mt-4"> <button id="cancelResi" class="px-4 py-2 text-gray-600 bg-gray-200 rounded-md">Batal</button> <button id="confirmResi" class="px-4 py-2 text-white bg-blue-500 rounded-md">Ya, Update</button> </div> </div> </div> <script> let currentPage = 1; let hasMoreData = true; let isFetching = false; let allOrders = []; let debounceTimer; let searchDebounceTimer; let currentTab = new URLSearchParams(window.location.search).get('tab') || 'pending'; const tabs = document.querySelectorAll('.tab-btn'); // Filter const filterBtn = document.querySelector('#filterDropdown button'); const filterMenu = document.querySelector('#filterMenu'); const filterText = document.getElementById("filterText"); const selectedFiltersContainer = document.getElementById("selectedFilters"); const filterCheckboxes = document.querySelectorAll('input[type="checkbox"]'); const urlParams = new URLSearchParams(window.location.search); // Search const searchInput = document.getElementById('searchInput'); // Date Filter const dateFilterDropdown = document.getElementById('dateFilterDropdown'); const dateFilterButton = dateFilterDropdown.querySelector('button'); const dateFilterMenu = document.getElementById('dateFilterMenu'); const dateFilterText = document.getElementById('dateFilterText'); const dateOptions = document.querySelectorAll('.date-option'); const customDateOption = document.getElementById('customDateOption'); const customDateIndicator = document.getElementById('customDateIndicator'); const calendarContainer = document.getElementById('calendarContainer'); const predefinedDateInfo = document.getElementById('predefinedDateInfo'); const dateDisplays = document.querySelectorAll('#dateDisplays > div'); // Current active option let activeOption = 'today'; // Initialize flatpickr calendar let flatpickrInstance = null; const BASE_URL_IMAGE = "<?= base_url('uploads/product') ?>"; const BASE_URL_PRODUCT = "<?= base_url('product') ?>"; const BASE_URL_FETCH_DATA = "<?= base_url('admin/orders/get_orders') ?>"; const BASE_URL = "<?= base_url('admin/orders') ?>"; const ADD_ORDER_URL = "<?= base_url('admin/orders/add-order') ?>"; const PRINT_LABEL_URL = "<?= base_url('admin/orders/print_label') ?>"; const ORDER_DETAIL_URL = "<?= base_url('admin/orders/detail') ?>"; let csrfToken = '<?= $this->security->get_csrf_hash(); ?>'; const csrfName = '<?= $this->security->get_csrf_token_name(); ?>'; 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(); }); }); // Sort sortFilter.addEventListener('change', () => { updateURLAndFetch(); updateSelectedFilters(); }); // Filter Menu filterBtn.addEventListener('click', () => { filterMenu.classList.toggle('hidden'); }); // Close dropdown when clicking outside document.addEventListener('click', (e) => { if (!e.target.closest('#filterDropdown')) { filterMenu.classList.add('hidden'); } }); // Handle checkbox changes filterCheckboxes.forEach(checkbox => { checkbox.addEventListener('change', () => { updateURLAndFetch(); updateFilterText(); updateSelectedFilters(); }); }); // Remove Filter window.removeFilter = function(param) { if (param === "sort") { sortFilter.value = ""; } else { filterCheckboxes.forEach(checkbox => { if (checkbox.dataset.param === param) { checkbox.checked = false; } }); } updateSelectedFilters(); updateURLAndFetch(); updateFilterText(); }; document.getElementById("resetFilters").addEventListener("click", resetAllFilters); // 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(allOrders); } else if (format === "excel") { downloadOrdersExcel(allOrders); } else if (format === "pdf") { downloadOrdersPDF(allOrders); } 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"); } }); // Marketing const marketingButton = document.querySelector('.marketing-button'); function updateMarketingButtonState(isActive) { if (isActive) { // State aktif marketingButton.classList.add('bg-purple-100', 'text-[#7A4397]', 'border-[#7A4397]', 'font-medium'); marketingButton.classList.remove('bg-white', 'border-gray-300', 'text-gray-700', 'hover:bg-gray-50'); } else { // State non-aktif marketingButton.classList.add('bg-white', 'border-gray-300', 'text-gray-700', 'hover:bg-gray-50'); marketingButton.classList.remove('bg-purple-100', 'text-[#7A4397]', 'border-[#7A4397]', 'font-medium'); } } const urlParams = new URLSearchParams(window.location.search); const isMarketingInitiallyActive = urlParams.get('department') === 'marketing'; updateMarketingButtonState(isMarketingInitiallyActive); marketingButton.addEventListener('click', function() { // Get current URL parameters const currentParams = new URLSearchParams(window.location.search); const isActive = currentParams.get('department') === 'marketing'; // Animasi efek klik marketingButton.classList.add('ring-1', 'ring-[#7A4397]', 'ring-opacity-50'); setTimeout(() => { marketingButton.classList.remove('ring-1', 'ring-[#7A4397]', 'ring-opacity-50'); }, 300); // Perbarui status tombol updateMarketingButtonState(!isActive); // Toggle parameter marketplace if (isActive) { // Jika aktif, hapus parameter updateURLAndFetch('department', null); } else { // Jika tidak aktif, tambahkan parameter updateURLAndFetch('department', 'marketing'); } }); // Tokopedia const tokopediaButton = document.querySelector('.tokopedia-button'); const websiteButton = document.querySelector('.website-button'); function updateButtonState(button, isActive) { if (isActive) { button.classList.add('bg-purple-100', 'text-[#7A4397]', 'border-[#7A4397]', 'font-medium'); button.classList.remove('bg-white', 'border-gray-300', 'text-gray-700', 'hover:bg-gray-50'); } else { button.classList.add('bg-white', 'border-gray-300', 'text-gray-700', 'hover:bg-gray-50'); button.classList.remove('bg-purple-100', 'text-[#7A4397]', 'border-[#7A4397]', 'font-medium'); } } function animateClick(button) { button.classList.add('ring-1', 'ring-[#7A4397]', 'ring-opacity-50'); setTimeout(() => { button.classList.remove('ring-1', 'ring-[#7A4397]', 'ring-opacity-50'); }, 300); } function handleMarketplaceClick(clickedButton, marketplaceValue) { const currentMarketplace = urlParams.get('marketplace'); const isActive = currentMarketplace === marketplaceValue; animateClick(clickedButton); if (isActive) { updateURLAndFetch('marketplace', null); updateButtonState(clickedButton, false); } else { updateURLAndFetch('marketplace', marketplaceValue); updateButtonState(clickedButton, true); // Nonaktifkan tombol lainnya const otherButton = clickedButton === tokopediaButton ? websiteButton : tokopediaButton; updateButtonState(otherButton, false); } } // Set state awal berdasarkan URL updateButtonState(tokopediaButton, urlParams.get('marketplace') === 'tokopedia'); updateButtonState(websiteButton, urlParams.get('marketplace') === 'website'); tokopediaButton.addEventListener('click', () => { handleMarketplaceClick(tokopediaButton, 'tokopedia'); }); websiteButton.addEventListener('click', () => { handleMarketplaceClick(websiteButton, 'website'); }); // Date Filter // Toggle dropdown dateFilterButton.addEventListener('click', function() { dateFilterMenu.classList.toggle('hidden'); }); // Close dropdown when clicking outside document.addEventListener('click', function(event) { if (!dateFilterDropdown.contains(event.target)) { dateFilterMenu.classList.add('hidden'); } }); // Handle date option selection dateOptions.forEach(option => { option.addEventListener('click', function() { const optionType = this.getAttribute('data-option'); // Set this as the active option activeOption = optionType; // Update visual indicators dateOptions.forEach(opt => { opt.classList.remove('bg-gray-100'); }); this.classList.add('bg-gray-100'); // Hide all date displays first dateDisplays.forEach(display => { display.classList.add('hidden'); }); if (optionType === 'customDate') { // Show calendar for custom date predefinedDateInfo.classList.add('hidden'); calendarContainer.classList.remove('hidden'); customDateIndicator.classList.remove('hidden'); // Initialize calendar if not already initializeCalendar(); } else { // Show the relevant date display predefinedDateInfo.classList.remove('hidden'); calendarContainer.classList.add('hidden'); customDateIndicator.classList.add('hidden'); // Show corresponding date info const correspondingDisplay = document.getElementById(`${optionType}DateDisplay`); if (correspondingDisplay) { correspondingDisplay.classList.remove('hidden'); } // Get date text to update the main button const dateInfo = document.getElementById(`${optionType}Date`); if (dateInfo) { const optionText = this.querySelector('div').innerText; dateFilterText.innerText = `${optionText} (${dateInfo.innerText})`; } else { dateFilterText.innerText = this.querySelector('div').innerText; } // Close dropdown after a slight delay to show the selection setTimeout(() => { dateFilterMenu.classList.add('hidden'); }, 300); // Tambahkan param date_filter ke URL dan fetch data updateURLAndFetch('date_filter', optionType); } }); }); function initializeCalendar() { if (flatpickrInstance === null && typeof flatpickr !== 'undefined') { // Create an input element for flatpickr if (!document.getElementById('dateRangePicker')) { const input = document.createElement('input'); input.type = 'text'; input.id = 'dateRangePicker'; input.className = 'hidden'; document.getElementById('flatpickrContainer').appendChild(input); } // Initialize flatpickr flatpickrInstance = flatpickr("#dateRangePicker", { mode: "range", inline: true, dateFormat: "d M Y", maxDate: "today", locale: { rangeSeparator: " - ", firstDayOfWeek: 1, weekdays: { shorthand: ['Min', 'Sen', 'Sel', 'Rab', 'Kam', 'Jum', 'Sab'], longhand: ['Minggu', 'Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat', 'Sabtu'] }, months: { shorthand: ['Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul', 'Agu', 'Sep', 'Okt', 'Nov', 'Des'], longhand: ['Januari', 'Februari', 'Maret', 'April', 'Mei', 'Juni', 'Juli', 'Agustus', 'September', 'Oktober', 'November', 'Desember'] } }, onChange: function(selectedDates, dateStr) { if (selectedDates.length === 2) { document.getElementById('customDateMessage').innerText = dateStr; } else if (selectedDates.length === 1) { document.getElementById('customDateMessage').innerText = 'Pilih tanggal akhir'; } else { document.getElementById('customDateMessage').innerText = 'Kamu belum pilih tanggal'; } }, onClose: function(selectedDates, dateStr) { if (selectedDates.length === 2) { // Update the custom date message document.getElementById('customDateMessage').innerText = dateStr; // Update the main filter text dateFilterText.innerText = `Pilih Tanggal (${dateStr})`; // Close the dropdown dateFilterMenu.classList.add('hidden'); // Simpan tanggal yang dipilih untuk digunakan dalam updateURLAndFetch const startDate = formatDateForApi(selectedDates[0]); const endDate = formatDateForApi(selectedDates[1]); // Update URL and fetch data dengan custom date updateURLAndFetch('date_filter', 'custom', { start_date: startDate, end_date: endDate }); } } }); // Customize calendar appearance const calendarElement = document.querySelector('.flatpickr-calendar'); if (calendarElement) { calendarElement.classList.add('shadow-none', 'border-0'); } } } // Helper function to format date for API function formatDateForApi(date) { return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`; } // Update date values on page load updateDateLabels(); // Set today as active by default const todayOption = document.querySelector('[data-option="today"]'); if (todayOption) { todayOption.classList.add('bg-gray-100'); document.getElementById('todayDateDisplay').classList.remove('hidden'); } window.addEventListener('scroll', () => { if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight - 500) { fetchOrders(new URLSearchParams(window.location.search), true); } }); document.getElementById('searchInput').addEventListener('input', function(e) { clearTimeout(searchDebounceTimer); searchDebounceTimer = setTimeout(() => { searchOrders(e.target.value); }, 250); }); // Add Order Handler const addOrderButton = document.querySelector('button[id="addOrder"]'); addOrderButton.addEventListener('click', function(event) { event.preventDefault(); window.open(`${ADD_ORDER_URL}`, '_blank'); }); const printLabelButtons = document.querySelector('button[id="printLabel"]'); printLabelButtons.addEventListener('click', function(event) { event.preventDefault(); window.open(`${PRINT_LABEL_URL}`, '_blank'); }); feather.replace(); updateURLAndFetch(); initInfiniteScroll(); }); 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); } } // Handle parameter date if (paramName === 'date_filter') { // Jika date_filter adalah custom, tambahkan extraParams if (value === 'custom' && extraParams) { for (const [key, val] of Object.entries(extraParams)) { params.set(key, val); } } else if (value !== 'custom') { // Hapus parameter start_date dan end_date jika bukan custom params.delete('start_date'); params.delete('end_date'); } } // Set parameter sort const selectedSort = document.getElementById('sortFilter').value; if (selectedSort) { params.set('sort', selectedSort); } else { params.delete('sort'); } // Collect all checkbox state first const checkboxParamMap = {}; // Inisialisasi semua parameter filter sebagai null document.querySelectorAll('input[type="checkbox"]').forEach(checkbox => { const param = checkbox.dataset.param; if (param && !checkboxParamMap[param]) { checkboxParamMap[param] = null; } }); // Setel nilai untuk checkbox yang dicentang document.querySelectorAll('input[type="checkbox"]:checked').forEach(checkbox => { const param = checkbox.dataset.param; const value = checkbox.dataset.value; if (param && value) { checkboxParamMap[param] = value; } }); // Terapkan parameter dari checkboxes for (const [param, value] of Object.entries(checkboxParamMap)) { if (value !== null) { params.set(param, value); } else { params.delete(param); } } // Reset pagination currentPage = 1; hasMoreData = true; allOrders = []; // Update URL const newUrl = `${window.location.pathname}?${params.toString()}`; history.pushState(null, '', newUrl); // Fetch products with new parameters (replace mode) fetchOrders(params, false); }, 300); } function fetchOrders(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', 250); const url = `${baseUrl}?${params.toString()}`; const progressBar = document.getElementById('progressBar'); progressBar.classList.remove('hidden'); fetch(url) .then(res => res.json()) .then(data => { progressBar.classList.add('hidden'); if (data.length === 0) { hasMoreData = false; if (!append) { const container = document.getElementById('orders-container'); container.innerHTML = '<div class="flex justify-center items-center p-8 text-gray-500">Pesanan udah gaada, kamu rajin banget ya.</div>'; } } else { if (!append) { allOrders = data; } else { allOrders = [...allOrders, ...data]; } console.log(allOrders); renderOrders(data, append); currentPage++; } isFetching = false; }) .catch(error => { progressBar.classList.add('hidden'); console.error('Error fetching orders:', error); isFetching = false; // Tampilkan pesan error const container = document.getElementById('orders-container'); container.innerHTML = '<div class="flex justify-center items-center p-8 text-red-500">Terjadi kesalahan saat mengambil data. Silakan coba lagi.</div>'; }); } function renderOrders(orders, append = false) { const container = document.getElementById('orders-container'); const loadingMore = document.getElementById('loading-more'); if (!append) { container.innerHTML = ''; // Clear existing content } if (!orders || orders.length === 0) { if (!append) { container.innerHTML = '<div class="flex justify-center items-center p-8 text-gray-500">Pesanan udah gaada, kamu rajin banget ya.</div>'; } loadingMore.classList.add('hidden'); return; } orders.forEach(order => { const items = order.items || []; const firstItem = items[0] || {}; const otherItemsCount = items.length - 1; const card = document.createElement('div'); card.className = 'bg-white rounded-lg shadow-md mb-4 overflow-hidden border border-gray-200'; card.innerHTML = ` <div class="flex flex-col md:flex-row md:justify-between gap-3 p-3 border-b border-gray-200 bg-gray-50"> <div class="flex flex-wrap items-center gap-2"> <span class="${order.payment_status_class} py-1 px-3 rounded-lg text-xs font-medium">${order.payment_status_text}</span> ${order.distribution_department === 'marketing' ? '<span class="bg-green-100 text-green-700 px-3 py-1 rounded-lg text-xs font-medium">Dari Marketing</span>' : ''} ${order.is_referral ? '<span class="bg-green-100 text-green-700 px-3 py-1 rounded-lg text-xs font-medium">Affiliate</span>' : ''} <span class="text-[#7A4397] font-bold text-sm"> ${order.tokopedia_invoice ? order.tokopedia_invoice : `Order ID: ${order.id_orders}`}</span> </div> <div class="flex flex-wrap items-center gap-4 text-sm"> <div class="flex items-center space-x-2 text-gray-700"> <i data-feather="user" class="w-4 h-4 text-[#7A4397]"></i> <span>${order.recipient_name}</span> </div> <div class="flex items-center space-x-2 text-gray-600"> <i data-feather="clock" class="w-4 h-4 text-[#7A4397]"></i> <span>${order.order_date_formatted || new Date(order.order_date).toLocaleString('id-ID')}</span> </div> </div> </div> <div class="grid grid-cols-3 gap-4 p-3"> <!-- Produk --> <div class="col-span-1"> <div class="flex mb-2"> <div class="w-20 h-20 mr-3 flex-shrink-0"> <img src="${BASE_URL_IMAGE}/${firstItem.product_image || 'default.jpg'}" alt="${firstItem.item_name}" onclick="showImagePopup('${BASE_URL_IMAGE}/${firstItem.product_image }', '${firstItem.item_name}')" class="w-full h-full object-cover rounded"> </div> <div class="flex-1"> <h4 class="font-medium text-sm text-gray-800">${firstItem.item_name || 'Nama Produk'}</h4> <p class="text-xs text-gray-500">${firstItem.sku || '-'}</p> <p class="text-sm text-gray-700 mt-1">${firstItem.quantity || 1} x Rp ${formatNumber(firstItem.item_price || 0)}</p> </div> </div> ${otherItemsCount > 0 ? ` <div class="more-products mt-2"> <button class="more-products-btn text-sm text-[#7A4397] flex items-center"> Lihat ${otherItemsCount} Produk Lainnya <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 ml-1" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" /> </svg> </button> </div>` : ''} </div> <!-- Alamat --> <div class="col-span-1"> <h3 class="text-sm font-medium text-gray-700 mb-1 flex items-center"> <i data-feather="map-pin" class="w-4 h-4 mr-2 text-[#7A4397]"></i> Dikirim ke </h3> <div class="text-sm text-gray-500 font-medium">${order.recipient_name || '-'} ${order.tokopedia_invoice ? '' : `(${order.phone})`}</div> <div class="text-sm text-gray-500"> ${order.address} </div> ${order.distribution_department === 'marketing' ? ` <div class="text-sm font-medium text-[#333] mt-2 mb-1"> Catatan dari Marketing </div> <div class="text-sm text-gray-500"> ${order.distribution_purpose || '-'} </div> ` : ''} ${order.customer_note ? ` <div class="text-sm font-medium text-[#333] mt-2 mb-1"> Catatan Pelanggan </div> <div class="text-sm text-gray-500"> ${order.customer_note} </div> ` : ''} <div class="mt-4 flex items-center space-x-2"> ${order.insurance_status === 'Yes' ? ` <span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-green-100 text-green-800"> <i data-feather="check" class="w-4 h-4 mr-2"></i> Asuransi Pengiriman </span> ` : ''} ${order.gift_receiver_name ? ` <span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-purple-100 text-purple-800"> <i data-feather="gift" class="w-4 h-4 mr-2"></i> Hadiah </span> ` : ''} </div> </div> <!-- Kurir & Metode Pembayaran--> <div class="col-span-1"> <div class="text-sm mb-4"> <div class="text-sm font-medium text-[#333] mb-1">Dibayar Pakai</div> <div class="text-sm text-gray-600">${order.payment_type ? order.payment_type.toLocaleLowerCase().replace(/\b\w/g, (char) => char.toUpperCase()) : '-'}</div> </div> <div class="text-sm font-medium text-[#333] mt-2"> Metode Pengiriman </div> <div class="text-sm text-gray-600 mb-2"> ${order.items .map(item => item.shipping_method_name) .filter((value, index, self) => self.indexOf(value) === index) .join(', ') .replace(/\b\w/g, char => char.toUpperCase())} </div> <div class="text-sm mt-2"> <div class="text-sm font-medium text-[#333] mb-1">Nomor Resi</div> <input type="text" id="order-resi" class="w-full pl-3 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-[#7A4397] focus:ring-opacity-50" value="${order.no_resi || '-'}" placeholder="Masukkan nomor resi" data-order-id="${order.id_orders}" ${order.payment_status_text !== 'Diproses' ? 'readonly' : ''}> </div> </div> </div> <div class="border-t border-gray-200 bg-gray-50"> <!-- Total Penjualan --> <div class="p-3 border-b border-gray-200"> <div class="flex justify-between items-center"> <div class="flex items-center font-medium text-gray-700"> <i data-feather="dollar-sign" class="w-4 h-4 mr-2 text-[#7A4397]"></i> Total Penjualan </div> <div class="font-semibold text-[#7A4397]">Rp ${formatNumber(order.grand_total_amount || 0)}</div> </div> </div> <!-- Catatan --> <div class="p-3"> <h3 class="text-sm font-medium text-gray-700 mb-3 flex items-center"> <i data-feather="file-text" class="w-4 h-4 mr-2 text-[#7A4397]"></i> Catatan Order </h3> <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4"> <!-- Admin Notes --> <div class="space-y-2"> <label for="order-notes" class="text-sm font-medium text-gray-700 flex items-center"> <i data-feather="eye-off" class="w-4 h-4 mr-2 text-[#7A4397]"></i> Admin Notes <span class="ml-1 text-xs text-gray-500">(hanya untuk admin)</span> </label> <div class="relative"> <input type="text" id="order-notes" data-order-id="${order.id_orders}" placeholder="Tambahkan catatan internal" class="w-full pl-3 pr-8 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-[#7A4397] focus:ring-opacity-75 transition-all" value="${order.admin_note || ''}"> </div> </div> <!-- Notes untuk Customer --> <div class="space-y-2"> <label for="customer-invoice-notes" class="text-sm font-medium text-gray-700 flex items-center"> <i data-feather="file-text" class="w-4 h-4 mr-2 text-[#7A4397]"></i> Notes untuk Customer <span class="ml-1 text-xs text-gray-500">(tampil di invoice)</span> </label> <div class="relative"> <input type="text" id="customer-invoice-notes" data-order-id="${order.id_orders}" placeholder="Tambahkan catatan untuk invoice customer" class="w-full pl-3 pr-8 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-[#7A4397] focus:ring-opacity-75 transition-all" value="${order.customer_invoice_note || ''}"> </div> </div> </div> <!-- Tombol Aksi --> <div class="flex flex-wrap gap-3 justify-between mt-4"> <div class="flex flex-wrap gap-2"> <button id="orderDetail" data-id="${order.id_orders}" class="px-3 py-2 border border-gray-300 rounded-md text-sm text-gray-600 flex items-center bg-white hover:bg-gray-50 transition-colors"> <i data-feather="file-text" class="w-4 h-4 mr-2 text-[#7A4397]"></i> Detail Pesanan </button> ${order.tokopedia_invoice ? '' : ` <button class="px-3 py-2 border border-gray-300 rounded-md text-sm text-gray-600 flex items-center whatsapp-chat-btn bg-white hover:bg-gray-50 transition-colors" data-phone="${order.phone}" data-name="${order.recipient_name}"> <i data-feather="message-circle" class="w-4 h-4 mr-2 text-green-600"></i> Chat Pembeli </button> `} <button id="printInvoice" data-id="${order.id_orders}" class="px-3 py-2 border border-gray-300 rounded-md text-sm text-gray-600 flex items-center bg-white hover:bg-gray-50 transition-colors"> <i data-feather="printer" class="w-4 h-4 mr-2 text-[#7A4397]"></i> Cetak Invoice </button> <button id="printDeliveryReceipt" data-id="${order.id_orders}" class="px-3 py-2 border border-gray-300 rounded-md text-sm text-gray-600 flex items-center bg-white hover:bg-gray-50 transition-colors"> <i data-feather="printer" class="w-4 h-4 mr-2 text-[#7A4397]"></i> Cetak Delivery Receipt </button> </div> <button id="changeStatusBtn" data-id="${order.id_orders}" data-status="${order.payment_status}" class="px-4 py-2 bg-[#7A4397] text-white rounded-md text-sm flex items-center hover:bg-[#693a84] transition-colors"> <i data-feather="refresh-cw" class="w-4 h-4 mr-2"></i> Ubah Status </button> </div> </div> </div> `; // Event listener untuk tombol "Lihat X Produk Lainnya" const moreProductsBtn = card.querySelector('.more-products-btn'); if (moreProductsBtn) { moreProductsBtn.addEventListener('click', function() { const productSection = this.closest('.col-span-1'); const isExpanded = productSection.querySelector('.additional-products'); if (isExpanded) { // Jika expanded, hapus produk tambahan productSection.querySelector('.additional-products').remove(); this.innerHTML = ` Lihat ${otherItemsCount} Produk Lainnya <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 ml-1" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" /> </svg> `; } else { // Tambahkan produk tambahan const productContainer = document.createElement('div'); productContainer.className = 'additional-products space-y-3 mt-3 pl-2 border-l-2 border-gray-200'; for (let i = 1; i < items.length; i++) { const item = items[i]; const additionalProduct = document.createElement('div'); additionalProduct.className = 'flex'; additionalProduct.innerHTML = ` <div class="w-16 h-16 mr-3 flex-shrink-0"> <img src="${BASE_URL_IMAGE}/${item.product_image || 'default.jpg'}" alt="${item.item_name}" onclick="showImagePopup('${BASE_URL_IMAGE}/${item.product_image}', '${item.title}')" class="w-full h-full object-cover rounded"> </div> <div class="flex-1"> <h4 class="font-medium text-sm text-gray-800">${item.item_name || 'Nama Produk'}</h4> <p class="text-xs text-gray-500">${item.sku || '-'}</p> <p class="text-sm text-gray-700 mt-1">${item.quantity || 1} x Rp ${formatNumber(item.item_price || 0)}</p> </div> `; productContainer.appendChild(additionalProduct); } this.parentNode.parentNode.insertBefore(productContainer, this.parentNode); this.innerHTML = ` Sembunyikan Produk <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 ml-1" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 15l7-7 7 7" /> </svg> `; } }); } container.appendChild(card); // Update status const statusButtons = card.querySelectorAll('button[id="changeStatusBtn"]'); statusButtons.forEach(button => { button.addEventListener('click', function() { const orderId = this.dataset.id; // Menggunakan data-id, bukan data-orderId const currentStatus = parseInt(this.dataset.status); // Pastikan tipe data integer // Tampilkan modal const modal = document.getElementById('changeStatusModal'); modal.classList.remove('hidden'); // Dapatkan semua radio buttons const statusRadios = modal.querySelectorAll('input[name="status"]'); // Atur visibility dan selected berdasarkan status saat ini statusRadios.forEach(radio => { const radioValue = parseInt(radio.value); const radioLabel = radio.closest('label'); // Reset tampilan radio button radio.disabled = false; radioLabel.classList.remove('opacity-50', 'cursor-not-allowed'); // Atur nilai yang dipilih sesuai dengan status saat ini radio.checked = (radioValue === currentStatus); // Terapkan aturan visibility berdasarkan status saat ini if (currentStatus === 0) { // Jika status Pending (0), semua opsi tersedia // radio dipilih sesuai status saat ini } else if (currentStatus === 1) { // Jika status Belum Dibayar (1), Pending tidak bisa dipilih if (radioValue === 0) { radio.disabled = true; radioLabel.classList.add('opacity-50', 'cursor-not-allowed'); } } else if (currentStatus === 2) { // Jika status Dibatalkan (2), Pending tidak bisa dipilih if (radioValue === 0) { radio.disabled = true; radioLabel.classList.add('opacity-50', 'cursor-not-allowed'); } } else if (currentStatus === 3) { // Jika status Sudah Dibayar (3), Pending dan Belum Dibayar tidak bisa dipilih if (radioValue === 0 || radioValue === 1) { radio.disabled = true; radioLabel.classList.add('opacity-50', 'cursor-not-allowed'); } } else if (currentStatus === 4) { // Jika status Diproses (4), hanya Selesai yang bisa dipilih if (radioValue !== 5) { radio.disabled = true; radioLabel.classList.add('opacity-50', 'cursor-not-allowed'); } } else if (currentStatus === 5) { // Jika status Selesai (5), tidak ada opsi yang bisa dipilih (semua disabled) // radio.disabled = true; // radioLabel.classList.add('opacity-50', 'cursor-not-allowed'); if (radioValue !== 2) { radio.disabled = true; radioLabel.classList.add('opacity-50', 'cursor-not-allowed'); } } if (radio.disabled) { radioLabel.classList.add('opacity-50', 'cursor-not-allowed'); radioDiv = radioLabel.querySelector('div'); radioDiv.classList.remove('hover:border-purple-500', 'hover:bg-purple-50', 'hover:border-orange-500', 'hover:bg-orange-50', 'hover:border-red-500', 'hover:bg-red-50', 'hover:border-green-500', 'hover:bg-green-50', 'hover:border-blue-500', 'hover:bg-blue-50'); } }); // Hapus event listener lama jika ada untuk mencegah duplikasi const confirmBtn = document.getElementById('confirmStatus'); const newConfirmBtn = confirmBtn.cloneNode(true); confirmBtn.parentNode.replaceChild(newConfirmBtn, confirmBtn); // Tambahkan event listener baru untuk tombol confirm newConfirmBtn.addEventListener('click', function() { const selectedStatus = modal.querySelector('input[name="status"]:checked'); if (selectedStatus) { const newStatus = selectedStatus.value; // Buat FormData untuk mengirim data const formData = new FormData(); formData.append('order_id', orderId); formData.append('status', newStatus); formData.append(csrfName, csrfToken); // Tampilkan loading this.innerHTML = '<svg class="animate-spin h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>'; this.disabled = true; // Kirim request AJAX fetch(`${BASE_URL}/updateOrderStatus`, { method: 'POST', headers: { 'X-Requested-With': 'XMLHttpRequest' }, body: formData }) .then(response => { if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return response.json(); }) .then(data => { if (data.success) { notyf.success(data.message || 'Status berhasil diubah'); // Refresh halaman setelah 1.5 detik setTimeout(() => { location.reload(); }, 1500); // Update CSRF token jika ada if (data.csrf_hash) { csrfToken = data.csrf_hash; } } else { throw new Error(data.message || 'Gagal mengubah status'); } }) .catch(error => { notyf.error(error.message); }) .finally(() => { // Sembunyikan modal modal.classList.add('hidden'); }); } }); // Hapus event listener lama untuk tombol cancel const cancelBtn = document.getElementById('cancelModal'); const newCancelBtn = cancelBtn.cloneNode(true); cancelBtn.parentNode.replaceChild(newCancelBtn, cancelBtn); // Tambahkan event listener baru untuk tombol cancel newCancelBtn.addEventListener('click', function() { modal.classList.add('hidden'); }); // Tambahkan handler untuk tombol close di bagian atas const closeBtn = document.getElementById('closeModal'); if (closeBtn) { closeBtn.addEventListener('click', function() { document.getElementById('changeStatusModal').classList.add('hidden'); }); } }); }); // Invoice const printInvoiceButtons = card.querySelectorAll('button[id="printInvoice"]'); printInvoiceButtons.forEach(button => { button.addEventListener('click', function(event) { event.preventDefault(); const orderId = this.getAttribute('data-id'); if (!orderId) { console.error('Order ID tidak ditemukan'); return; } // Arahkan ke function generate_invoice dengan ID pesanan window.open(`${BASE_URL}/generate_invoice/${orderId}`, '_blank'); }); }); // Delivery Receipt const deliveryReceiptButtons = card.querySelectorAll('button[id="printDeliveryReceipt"]'); deliveryReceiptButtons.forEach(button => { button.addEventListener('click', function(event) { event.preventDefault(); const orderId = this.getAttribute('data-id'); if (!orderId) { console.error('Order ID tidak ditemukan'); return; } // Arahkan ke function generate_invoice dengan ID pesanan window.open(`${BASE_URL}/generate_delivery_receipt/${orderId}`, '_blank'); }); }); // Resi const resiInputs = card.querySelectorAll('input[id="order-resi"]'); const modalResi = document.getElementById('modalResi'); const confirmBtnResi = document.getElementById('confirmResi'); const cancelBtnResi = document.getElementById('cancelResi'); let selectedResiInput = null; let newResiValue = ''; let previousResiValue = ''; resiInputs.forEach(input => { input.addEventListener('focusout', function(event) { const orderId = this.dataset.orderId; const previousValue = this.getAttribute('data-prev') || this.defaultValue || '-'; const newValue = this.value.trim(); // Jika nilai tidak berubah atau kosong, jangan lakukan update if (newValue === previousValue || newValue === '' || newValue === '-') return; // Simpan elemen input yang sedang diedit selectedResiInput = this; newResiValue = newValue; previousResiValue = previousValue; // Tambahkan efek loading this.disabled = true; this.classList.add('opacity-50'); // Tampilkan modal konfirmasi modalResi.classList.remove('hidden'); }); }); // Jika pengguna membatalkan cancelBtnResi.addEventListener('click', () => { if (selectedResiInput) selectedResiInput.value = previousResiValue; // Kembalikan ke nilai awal modalResi.classList.add('hidden'); }); // Jika pengguna mengonfirmasi update resi confirmBtnResi.addEventListener('click', () => { if (!selectedResiInput) return; const orderId = selectedResiInput.dataset.orderId; selectedResiInput.setAttribute('data-prev', newResiValue); selectedResiInput.disabled = true; selectedResiInput.classList.add('opacity-50'); const formData = new FormData(); formData.append('order_id', orderId); formData.append('no_resi', newResiValue); formData.append(csrfName, csrfToken); fetch(`${BASE_URL}/updateResi`, { method: 'POST', headers: { 'X-Requested-With': 'XMLHttpRequest' }, body: formData }) .then(response => { if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); return response.json(); }) .then(data => { if (data.success) { notyf.success(data.message); setTimeout(() => { location.reload(); }, 2000); if (data.csrf_hash) csrfToken = data.csrf_hash; } else { throw new Error(data.message || 'Gagal menyimpan nomor resi'); } }) .catch(error => { notyf.error(error); selectedResiInput.value = previousResiValue; }) .finally(() => { selectedResiInput.disabled = false; selectedResiInput.classList.remove('opacity-50'); modalResi.classList.add('hidden'); }); }); // Customer Invoice Notes const notesInputs = card.querySelectorAll('input[id="order-notes"]'); notesInputs.forEach(input => { input.addEventListener('focusout', function(event) { const orderId = this.dataset.orderId; const previousValue = this.getAttribute('data-prev') || this.defaultValue || ''; const newValue = this.value.trim(); // Jika nilai tidak berubah, jangan lakukan update if (newValue === previousValue) return; // Simpan nilai baru sebagai nilai sebelumnya untuk perbandingan berikutnya this.setAttribute('data-prev', newValue); // Tambahkan efek loading this.disabled = true; this.classList.add('opacity-50'); // Buat FormData untuk mengirim data const formData = new FormData(); formData.append('order_id', orderId); formData.append('notes', newValue); formData.append(csrfName, csrfToken); // Request ke backend dengan fetch API fetch(`${BASE_URL}/updateNotes`, { method: 'POST', headers: { 'X-Requested-With': 'XMLHttpRequest' // Penting untuk CI3 }, body: formData }) .then(response => { if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return response.json(); }) .then(data => { if (data.success) { notyf.success(data.message || 'Catatan berhasil disimpan'); if (data.csrf_hash) { csrfToken = data.csrf_hash; } } else { throw new Error(data.message || 'Gagal menyimpan catatan'); } }) .catch(error => { notyf.error(error.message); this.value = previousValue; }) .finally(() => { this.disabled = false; this.classList.remove('opacity-50'); }); }); }); // Notes const customerInvoiceNotes = card.querySelectorAll('input[id="customer-invoice-notes"]'); customerInvoiceNotes.forEach(input => { input.addEventListener('focusout', function(event) { const orderId = this.dataset.orderId; const previousValue = this.getAttribute('data-prev') || this.defaultValue || ''; const newValue = this.value.trim(); // Jika nilai tidak berubah, jangan lakukan update if (newValue === previousValue) return; // Simpan nilai baru sebagai nilai sebelumnya untuk perbandingan berikutnya this.setAttribute('data-prev', newValue); // Tambahkan efek loading this.disabled = true; this.classList.add('opacity-50'); // Buat FormData untuk mengirim data const formData = new FormData(); formData.append('order_id', orderId); formData.append('notes', newValue); formData.append(csrfName, csrfToken); // Request ke backend dengan fetch API fetch(`${BASE_URL}/updateInvoiceNotes`, { method: 'POST', headers: { 'X-Requested-With': 'XMLHttpRequest' // Penting untuk CI3 }, body: formData }) .then(response => { if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return response.json(); }) .then(data => { if (data.success) { notyf.success(data.message || 'Catatan berhasil disimpan'); if (data.csrf_hash) { csrfToken = data.csrf_hash; } } else { throw new Error(data.message || 'Gagal menyimpan catatan'); } }) .catch(error => { notyf.error(error.message); this.value = previousValue; }) .finally(() => { this.disabled = false; this.classList.remove('opacity-50'); }); }); }); // Order Detail const orderDetailButtons = card.querySelectorAll('button[id="orderDetail"]'); orderDetailButtons.forEach(button => { button.addEventListener('click', function(event) { event.preventDefault(); const orderId = this.getAttribute('data-id'); if (!orderId) { console.error('Order ID tidak ditemukan'); return; } window.open(`${ORDER_DETAIL_URL}/${orderId}`, '_blank'); }); }); }); loadingMore.classList.add('hidden'); feather.replace(); const chatButtons = document.querySelectorAll('.whatsapp-chat-btn'); chatButtons.forEach(button => { button.addEventListener('click', (e) => { let phoneNumber = e.target.closest('.whatsapp-chat-btn').dataset.phone; const customerName = e.target.closest('.whatsapp-chat-btn').dataset.name; if (phoneNumber.startsWith('08')) { phoneNumber = `62${phoneNumber.substring(1)}`; } else if (phoneNumber.startsWith('8')) { phoneNumber = `62${phoneNumber}`; } const whatsappURL = `https://api.whatsapp.com/send?phone=${phoneNumber}&text=Halo kak ${customerName}`; window.open(whatsappURL, '_blank'); }); }); } function showImagePopup(imageSrc, imageAlt) { // Cek apakah sudah ada popup yang terbuka const existingPopup = document.getElementById('imagePopup'); if (existingPopup) { existingPopup.remove(); } // Buat elemen popup const popup = document.createElement('div'); popup.id = 'imagePopup'; popup.className = 'fixed inset-0 flex items-center justify-center z-[1000] bg-black bg-opacity-75 transition-opacity duration-300'; // Buat konten popup popup.innerHTML = ` <div class="relative max-w-4xl max-h-[90vh] rounded-lg overflow-hidden bg-white"> <div class="absolute top-4 right-4"> <button onclick="closeImagePopup()" class="text-white bg-gray-800 rounded-full p-2 hover:bg-gray-900 transition-colors"> <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path> </svg> </button> </div> <img src="${imageSrc}" alt="${imageAlt}" class="max-h-[85vh] object-contain"> <div class="p-4 bg-white"> <h3 class="text-lg font-medium text-gray-900">${imageAlt}</h3> </div> </div> `; // Tambahkan popup ke body document.body.appendChild(popup); // Tambahkan event listener untuk menutup popup jika user klik di luar gambar popup.addEventListener('click', function(e) { if (e.target === popup) { closeImagePopup(); } }); } // Fungsi untuk menutup popup gambar function closeImagePopup() { const popup = document.getElementById('imagePopup'); if (popup) { popup.classList.add('opacity-0'); setTimeout(() => { popup.remove(); }, 300); } } // Fungsi helper untuk memformat angka function formatNumber(number) { return new Intl.NumberFormat('id-ID').format(number); } function resetAllFilters() { sortFilter.value = ""; // Reset checkbox filter filterCheckboxes.forEach(checkbox => { checkbox.checked = false; }); // Update tampilan filter yang dipilih updateSelectedFilters(); updateURLAndFetch(); updateFilterText(); } function updateFilterText() { const checkedCount = document.querySelectorAll(".filter-checkbox:checked").length; filterText.textContent = checkedCount > 0 ? `${checkedCount} Filter Terpilih` : "Filter"; } function updateSelectedFilters() { const resetFilters = document.getElementById("resetFilters"); // Menyimpan elemen resetFilters di dalam selectedFiltersContainer selectedFiltersContainer.innerHTML = ""; let hasFilters = false; selectedFiltersContainer.appendChild(resetFilters); // Menambahkan filter 'sort' const selectedSort = sortFilter.value; if (selectedSort) { hasFilters = true; const sortTag = document.createElement("div"); sortTag.className = "flex items-center border border-[#7A4397] rounded-lg px-3 py-1 bg-gray-100 text-sm"; sortTag.innerHTML = ` ${sortFilter.options[sortFilter.selectedIndex].text} <button class="ml-2 text-gray-500 hover:text-gray-700" onclick="removeFilter('sort')">×</button> `; selectedFiltersContainer.appendChild(sortTag); } // Menambahkan filter dari checkbox filterCheckboxes.forEach(checkbox => { if (checkbox.checked) { hasFilters = true; const filterTag = document.createElement("div"); filterTag.className = "flex items-center border border-[#7A4397] rounded-lg px-3 py-1 bg-gray-100 text-sm"; filterTag.innerHTML = ` ${checkbox.nextElementSibling.innerText} <button class="ml-2 text-gray-500 hover:text-gray-700" onclick="removeFilter('${checkbox.dataset.param}')">×</button> `; selectedFiltersContainer.appendChild(filterTag); } }); // Menampilkan atau menyembunyikan tombol reset filter if (hasFilters) { resetFilters.classList.remove("hidden"); } else { resetFilters.classList.add("hidden"); } } function updateDateLabels() { const now = new Date(); const today = new Date(now); const yesterday = new Date(now); yesterday.setDate(yesterday.getDate() - 1); // Update the month title document.getElementById('currentMonthTitle').innerText = today.toLocaleDateString('id-ID', { month: 'long', year: 'numeric' }); // Format function for dates function formatDate(date) { return date.toLocaleDateString('id-ID', { day: '2-digit', month: 'short', year: 'numeric' }).replace('.', ''); } function formatDateRange(start, end) { return `${formatDate(start)} - ${formatDate(end)}`; } // Today with real time const hour = now.getHours(); const minute = now.getMinutes(); document.getElementById('todayDate').innerText = `${formatDate(today)} (00:00 - ${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')})`; // Yesterday document.getElementById('yesterdayDate').innerText = formatDate(yesterday); // Last 7 days const last7Start = new Date(now); last7Start.setDate(last7Start.getDate() - 7); const last7End = new Date(yesterday); document.getElementById('last7daysDate').innerText = formatDateRange(last7Start, last7End); // Last 30 days const last30Start = new Date(now); last30Start.setDate(last30Start.getDate() - 30); const last30End = new Date(yesterday); document.getElementById('last30daysDate').innerText = formatDateRange(last30Start, last30End); // This month const thisMonthStart = new Date(now.getFullYear(), now.getMonth(), 1); const thisMonthEnd = new Date(now.getFullYear(), now.getMonth() + 1, 0); document.getElementById('thisMonthDate').innerText = formatDateRange(thisMonthStart, thisMonthEnd); } // Initialize the scroll listener function initInfiniteScroll() { window.addEventListener('scroll', function() { // Check if we're near the bottom of the page if (window.innerHeight + window.scrollY >= document.body.offsetHeight - 500) { // If we're within 500px of the bottom, load more data if (!isFetching && hasMoreData) { // Get current URL parameters const params = new URLSearchParams(window.location.search); fetchOrders(params, true); // true means append instead of replace } } }); } function searchOrders(searchTerm) { // Reset data pencarian dan pagination currentPage = 1; hasMoreData = true; allOrders = []; // Ambil parameter URL saat ini const params = new URLSearchParams(window.location.search); // Tambahkan parameter pencarian atau hapus jika kosong if (searchTerm && searchTerm.trim() !== '') { params.set('search', searchTerm.trim()); } else { params.delete('search'); } // Update URL const newUrl = `${window.location.pathname}?${params.toString()}`; history.pushState(null, '', newUrl); // Fetch dengan parameter pencarian fetchOrders(params, false); } // Download PDF function downloadOrdersPDF(allOrders) { const { jsPDF } = window.jspdf; const doc = new jsPDF(); const today = new Date().toISOString().split("T")[0]; doc.setFontSize(18); doc.text("Laporan Pesanan", 14, 15); doc.setFontSize(12); doc.text(`Dicetak Tanggal: ${today}`, 14, 22); // Tambahkan informasi filter let filtersText = "Filter: "; const urlParams = new URLSearchParams(window.location.search); const tab = urlParams.get("tab") || "Semua"; filtersText += `Status: ${tab.charAt(0).toUpperCase() + tab.slice(1)}, `; const startDate = urlParams.get("start_date"); const endDate = urlParams.get("end_date"); const dateFilter = urlParams.get("date_filter"); // Mapping untuk label date_filter const dateFilterLabels = { today: "Hari ini", yesterday: "Kemarin", last7days: "7 hari terakhir", last30days: "30 hari terakhir", thisMonth: "Bulan ini", thisYear: "Tahun ini", customDate: "Rentang Tanggal" }; if (startDate && endDate) { filtersText += `Tanggal: ${startDate} - ${endDate}, `; } else if (dateFilter && dateFilterLabels[dateFilter]) { filtersText += `Tanggal: ${dateFilterLabels[dateFilter]}, `; } const paymentFilters = []; if (urlParams.get("isBCATransfer")) paymentFilters.push("Transfer BCA"); if (urlParams.get("isMandiriTransfer")) paymentFilters.push("Transfer Mandiri"); if (urlParams.get("isPaypal")) paymentFilters.push("PayPal"); if (urlParams.get("isDoku")) paymentFilters.push("DOKU"); if (paymentFilters.length > 0) { filtersText += `Metode Pembayaran: ${paymentFilters.join(", ")}, `; } const shippingFilters = []; if (urlParams.get("isRegularShipping")) shippingFilters.push("Regular"); if (urlParams.get("isTwoHourShipping")) shippingFilters.push("2 Hour Delivery"); if (urlParams.get("isOneDayShipping")) shippingFilters.push("One Day Service"); if (urlParams.get("isNextDayShipping")) shippingFilters.push("Next Day Service"); if (shippingFilters.length > 0) { filtersText += `Metode Pengiriman: ${shippingFilters.join(", ")}, `; } const insurance = urlParams.get("usingInsurance") ? "Pakai Asuransi" : urlParams.get("notUsingInsurance") ? "Ga Pakai Asuransi" : ""; if (insurance) { filtersText += `Asuransi: ${insurance}, `; } filtersText = filtersText.replace(/, $/, ""); const maxWidth = 180; // Lebar maksimal teks sebelum dipotong const textLines = doc.splitTextToSize(filtersText, maxWidth); // Memecah teks menjadi beberapa baris let y = 30; // Posisi awal teks // Loop untuk mencetak setiap baris teks di posisi Y yang bertambah textLines.forEach(line => { doc.text(line, 14, y); y += 6; // Menambah posisi Y agar teks turun ke bawah }); y += 10; let counter = 1; allOrders.forEach((order) => { const orderSource = order.tokopedia_invoice ? "Marketplace (Tokopedia)" : "Website"; doc.setFontSize(12); doc.setFont(undefined, "bold"); doc.text(`#${counter} - Order ID: ${order.id_orders} (${orderSource})`, 14, y); doc.setFont(undefined, "normal"); y += 6; doc.text(`Penerima: ${order.recipient_name}`, 14, y); y += 6; doc.text(`Alamat Pengiriman: ${order.subdistrict}, ${order.district}, ${order.province}`, 14, y); y += 6; doc.text(`Total Belanja: Rp ${new Intl.NumberFormat("id-ID").format(order.grand_total_amount)}`, 14, y); y += 6; doc.text(`Metode Pembayaran: ${order.payment_type}`, 14, y); y += 6; doc.text(`Biaya Pengiriman: Rp ${new Intl.NumberFormat("id-ID").format(order.shipping_fee)}`, 14, y); y += 6; doc.text(`Status Pesanan: ${order.payment_status_text}`, 14, y); y += 6; doc.text(`Tanggal: ${order.order_date_formatted}`, 14, y); y += 8; // Tabel Produk const headers = [ ["Nama Produk", "Qty", "Harga", "Subtotal"] ]; let data = order.items.map((item) => [ item.item_name, item.quantity, `Rp ${new Intl.NumberFormat("id-ID").format(item.item_price)}`, `Rp ${new Intl.NumberFormat("id-ID").format(item.subtotal)}` ]); if (y > 230) { doc.addPage(); y = 20; } doc.autoTable({ head: headers, body: data, startY: y, theme: "striped", styles: { fontSize: 10, cellPadding: 2 }, headStyles: { fillColor: [122, 67, 151], textColor: 255, fontStyle: "bold" }, alternateRowStyles: { fillColor: [240, 240, 240] }, margin: { left: 14, right: 14 }, }); y = doc.autoTable.previous.finalY + 10; counter++; }); doc.save(`Laporan_Pesanan_${today}.pdf`); } // Download Excel function downloadOrdersExcel(allOrders) { const urlParams = new URLSearchParams(window.location.search); const today = new Date().toISOString().split("T")[0]; // Ambil informasi filter yang sama dengan PDF let filtersText = "Filter: "; const tab = urlParams.get("tab") || "Semua"; filtersText += `Status: ${tab.charAt(0).toUpperCase() + tab.slice(1)}, `; const startDate = urlParams.get("start_date"); const endDate = urlParams.get("end_date"); const dateFilter = urlParams.get("date_filter"); const dateFilterLabels = { today: "Hari ini", yesterday: "Kemarin", last7days: "7 hari terakhir", last30days: "30 hari terakhir", thisMonth: "Bulan ini", thisYear: "Tahun ini", customDate: "Rentang Tanggal" }; if (startDate && endDate) { filtersText += `Tanggal: ${startDate} - ${endDate}, `; } else if (dateFilter && dateFilterLabels[dateFilter]) { filtersText += `Tanggal: ${dateFilterLabels[dateFilter]}, `; } const paymentFilters = []; if (urlParams.get("isBCATransfer")) paymentFilters.push("Transfer BCA"); if (urlParams.get("isMandiriTransfer")) paymentFilters.push("Transfer Mandiri"); if (urlParams.get("isPaypal")) paymentFilters.push("PayPal"); if (urlParams.get("isDoku")) paymentFilters.push("DOKU"); if (paymentFilters.length > 0) { filtersText += `Metode Pembayaran: ${paymentFilters.join(", ")}, `; } const shippingFilters = []; if (urlParams.get("isRegularShipping")) shippingFilters.push("Regular"); if (urlParams.get("isTwoHourShipping")) shippingFilters.push("2 Hour Delivery"); if (urlParams.get("isOneDayShipping")) shippingFilters.push("One Day Service"); if (urlParams.get("isNextDayShipping")) shippingFilters.push("Next Day Service"); if (shippingFilters.length > 0) { filtersText += `Metode Pengiriman: ${shippingFilters.join(", ")}, `; } const insurance = urlParams.get("usingInsurance") ? "Pakai Asuransi" : urlParams.get("notUsingInsurance") ? "Ga Pakai Asuransi" : ""; if (insurance) { filtersText += `Asuransi: ${insurance}, `; } filtersText = filtersText.replace(/, $/, ""); // Buat data untuk Excel let excelData = []; // Baris pertama untuk informasi filter excelData.push([filtersText]); // Tambahkan header dengan warna const header = [ "No", "ID Pesanan", "Sumber", "Penerima", "Alamat", "Total Belanja", "Biaya Pengiriman", "Metode Pembayaran", "Status", "Tanggal" ]; excelData.push(header); // Variabel untuk menghitung total keseluruhan let grandTotalSum = 0; // Status pembayaran yang dihitung untuk total keseluruhan const validPaymentStatuses = ["Sudah Dibayar", "Sedang Dikirim", "Diproses", "Selesai"]; // Tambahkan data pesanan allOrders.forEach((order, index) => { const orderSource = order.tokopedia_invoice ? "Marketplace (Tokopedia)" : "Website"; // Tambahkan total belanja ke total keseluruhan hanya jika status pembayaran valid if (validPaymentStatuses.includes(order.payment_status_text)) { grandTotalSum += parseFloat(order.grand_total_amount); } excelData.push([ index + 1, order.id_orders, orderSource, order.recipient_name, `${order.subdistrict}, ${order.district}, ${order.province}`, `Rp ${new Intl.NumberFormat("id-ID").format(order.grand_total_amount)}`, `Rp ${new Intl.NumberFormat("id-ID").format(order.shipping_fee)}`, order.payment_type, order.payment_status_text, order.order_date_formatted ]); // Tambahkan data produk di bawahnya order.items.forEach((item) => { excelData.push([ "", "Produk:", item.item_name, `Qty: ${item.quantity}`, `Rp ${new Intl.NumberFormat("id-ID").format(item.item_price)}`, `Subtotal: Rp ${new Intl.NumberFormat("id-ID").format(item.subtotal)}` ]); }); // Baris kosong sebagai pemisah antar pesanan excelData.push([]); }); // Tambahkan baris dengan total keseluruhan di bagian akhir excelData.push([]); excelData.push([ "", "", "", "", "TOTAL KESELURUHAN:", `Rp ${new Intl.NumberFormat("id-ID").format(grandTotalSum)}`, "", "", "", "" ]); // Buat worksheet & workbook const ws = XLSX.utils.aoa_to_sheet(excelData); const wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, "Laporan Pesanan"); // Simpan file Excel XLSX.writeFile(wb, `Laporan_Pesanan_${today}.xlsx`); } // Download CSV function downloadOrdersCSV(allOrders) { const urlParams = new URLSearchParams(window.location.search); const today = new Date().toISOString().split("T")[0]; // Ambil informasi filter yang sama dengan PDF let filtersText = "Filter: "; const tab = urlParams.get("tab") || "Semua"; filtersText += `Status: ${tab.charAt(0).toUpperCase() + tab.slice(1)}, `; const startDate = urlParams.get("start_date"); const endDate = urlParams.get("end_date"); const dateFilter = urlParams.get("date_filter"); const dateFilterLabels = { today: "Hari ini", yesterday: "Kemarin", last7days: "7 hari terakhir", last30days: "30 hari terakhir", thisMonth: "Bulan ini", thisYear: "Tahun ini", customDate: "Rentang Tanggal" }; if (startDate && endDate) { filtersText += `Tanggal: ${startDate} - ${endDate}, `; } else if (dateFilter && dateFilterLabels[dateFilter]) { filtersText += `Tanggal: ${dateFilterLabels[dateFilter]}, `; } const paymentFilters = []; if (urlParams.get("isBCATransfer")) paymentFilters.push("Transfer BCA"); if (urlParams.get("isMandiriTransfer")) paymentFilters.push("Transfer Mandiri"); if (urlParams.get("isPaypal")) paymentFilters.push("PayPal"); if (urlParams.get("isDoku")) paymentFilters.push("DOKU"); if (paymentFilters.length > 0) { filtersText += `Metode Pembayaran: ${paymentFilters.join(", ")}, `; } const shippingFilters = []; if (urlParams.get("isRegularShipping")) shippingFilters.push("Regular"); if (urlParams.get("isTwoHourShipping")) shippingFilters.push("2 Hour Delivery"); if (urlParams.get("isOneDayShipping")) shippingFilters.push("One Day Service"); if (urlParams.get("isNextDayShipping")) shippingFilters.push("Next Day Service"); if (shippingFilters.length > 0) { filtersText += `Metode Pengiriman: ${shippingFilters.join(", ")}, `; } const insurance = urlParams.get("usingInsurance") ? "Pakai Asuransi" : urlParams.get("notUsingInsurance") ? "Ga Pakai Asuransi" : ""; if (insurance) { filtersText += `Asuransi: ${insurance}, `; } filtersText = filtersText.replace(/, $/, ""); // Buat array untuk CSV let csvData = []; // Tambahkan informasi filter di baris pertama csvData.push([filtersText]); // Header CSV csvData.push(["No", "ID Pesanan", "Sumber", "Penerima", "Alamat", "Total Belanja", "Metode Pembayaran", "Status", "Tanggal"]); // Tambahkan data pesanan allOrders.forEach((order, index) => { const orderSource = order.tokopedia_invoice ? "Marketplace (Tokopedia)" : "Website"; csvData.push([ index + 1, order.id_orders, orderSource, order.recipient_name, `${order.subdistrict}, ${order.district}, ${order.province}`, `Rp ${new Intl.NumberFormat("id-ID").format(order.grand_total_amount)}`, order.payment_type, order.payment_status_text, order.order_date_formatted ]); // Tambahkan data produk di bawahnya order.items.forEach((item) => { csvData.push([ "", "Produk:", item.item_name, `Qty: ${item.quantity}`, `Rp ${new Intl.NumberFormat("id-ID").format(item.item_price)}`, `Subtotal: Rp ${new Intl.NumberFormat("id-ID").format(item.subtotal)}` ]); }); // Baris kosong sebagai pemisah antar pesanan csvData.push([]); }); // Convert array ke CSV string let csvContent = csvData.map(row => row.map(cell => `"${cell}"`).join(",")).join("\n"); // Buat file CSV const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" }); const link = document.createElement("a"); const url = URL.createObjectURL(blob); link.setAttribute("href", url); link.setAttribute("download", `Laporan_Pesanan_${today}.csv`); document.body.appendChild(link); link.click(); document.body.removeChild(link); } </script>