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 : |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Shipping Label Printer</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script> <style> body { font-family: Arial, sans-serif; margin: 20px; background-color: #f5f5f5; } .container { max-width: 800px; margin: 0 auto; background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); } .controls { margin-bottom: 20px; padding: 20px; background: #f8f9fa; border-radius: 6px; } .form-group { margin-bottom: 15px; } label { display: block; margin-bottom: 5px; font-weight: bold; color: #333; } input[type="text"] { width: 200px; padding: 8px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; } button { background: #7A4397; color: white; padding: 10px 20px; border: none; border-radius: 4px; cursor: pointer; font-size: 14px; margin-right: 10px; } button:hover { background: #66357a; } button:disabled { background: #6c757d; cursor: not-allowed; } .preview { margin-top: 20px; border: 1px solid #ddd; border-radius: 4px; background: white; } .preview-header { background: #e9ecef; padding: 10px; border-bottom: 1px solid #ddd; font-weight: bold; } .labels-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; padding: 10px; } .label { border: 1px dashed #7A4397; padding: 8px; font-size: 10px; height: 350px; position: relative; background: #fafafa; } .label-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; padding: 5px; border-bottom: 1px solid #ddd; background-color: #7A4397; } .logo { display: flex; align-items: center; height: 30px; } .logo img { max-height: 30px; max-width: 120px; object-fit: contain; } .logo span { font-weight: bold; font-size: 12px; color: #007bff; } .order-info { font-size: 9px; color: white; } .address-section { margin-bottom: 8px; } .shipping-info { display: flex; justify-content: space-between; flex-wrap: wrap; margin-bottom: 10px; padding-bottom: 8px; border-bottom: 1px solid #ddd; } .shipping-pair { display: flex; flex-direction: column; min-width: 80px; text-align: left; } .shipping-pair .shipping-label { font-weight: bold; color: #333; font-size: 9px; margin-bottom: 2px; } .shipping-pair .shipping-value { color: #444; font-size: 8px; } .address-title { font-weight: bold; font-size: 9px; color: #333; margin-bottom: 2px; } .address-content { font-size: 8px; line-height: 1.2; color: #555; } .cut-line { border-top: 1px dashed #999; margin: 8px 0; position: relative; } .cut-line::before { content: "✂ Cut here"; position: absolute; right: 0; top: -8px; font-size: 7px; color: #666; background: #fafafa; padding: 0 5px; } .items-list { font-size: 7px; line-height: 1.2; } .item { margin-bottom: 2px; display: flex; justify-content: space-between; } .loading { text-align: center; padding: 20px; color: #666; } .error { color: #dc3545; background: #f8d7da; padding: 10px; border-radius: 4px; margin-bottom: 15px; } .success { color: #155724; background: #d4edda; padding: 10px; border-radius: 4px; margin-bottom: 15px; } .remove-label-btn { background: #ff4444; color: white; border: none; border-radius: 50%; width: 24px; height: 24px; font-size: 12px; font-weight: bold; cursor: pointer; position: absolute; top: 5px; right: 5px; display: flex; align-items: center; justify-content: center; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); transition: all 0.2s ease; z-index: 10; padding: 0; line-height: 1; min-width: 20px; } .remove-label-btn:hover { background: #cc0000; transform: scale(1.1); box-shadow: 0 3px 6px rgba(0, 0, 0, 0.3); } .remove-label-btn:active { transform: scale(0.95); } /* Print styles */ @media print { body * { visibility: hidden; } .print-area, .print-area * { visibility: visible; } .print-area { position: absolute; left: 0; top: 0; width: 100%; } } </style> </head> <body> <div class="container"> <h1>Shipping Label</h1> <div class="controls"> <div class="form-group"> <label for="orderId">Order ID:</label> <input type="text" id="orderId" placeholder="Enter order ID" value="12345"> <span>Total Pesanan: <span id="orderCount">0</span></span> </div> <div class="form-group" style="display: none;"> <label for="apiEndpoint">API Endpoint:</label> <input type="text" id="apiEndpoint" placeholder="/api/orders/" value="<?= base_url('admin/orders/get_print_label_data/') ?>" disabled> </div> <button id="loadOrder">Load Order</button> <button id="printLabel" disabled>Print Labels (4 per page)</button> <!-- <button id="generatePDF" disabled>Generate PDF</button> --> <button id="clearAll">Clear All Orders</button> </div> <div id="message"></div> <div class="preview"> <div class="preview-header">Label Preview (4 labels per A4 page)</div> <div id="previewContent"> <div class="loading">Load an order to see preview</div> </div> </div> </div> <script> class ShippingLabelPrinter { constructor() { this.ordersData = []; // Array to store multiple orders this.initializeElements(); this.attachEventListeners(); } initializeElements() { this.orderIdInput = document.getElementById('orderId'); this.apiEndpointInput = document.getElementById('apiEndpoint'); this.loadOrderBtn = document.getElementById('loadOrder'); this.printLabelBtn = document.getElementById('printLabel'); // this.generatePDFBtn = document.getElementById('generatePDF'); this.clearAllBtn = document.getElementById('clearAll'); // Add clear all button this.messageDiv = document.getElementById('message'); this.previewContent = document.getElementById('previewContent'); this.orderCountSpan = document.getElementById('orderCount'); // Add order count display } attachEventListeners() { this.loadOrderBtn.addEventListener('click', () => this.loadOrder()); this.printLabelBtn.addEventListener('click', () => this.printLabels()); // this.generatePDFBtn.addEventListener('click', () => this.generatePDF()); if (this.clearAllBtn) { this.clearAllBtn.addEventListener('click', () => this.clearAllOrders()); } } showMessage(message, type = 'info') { this.messageDiv.innerHTML = `<div class="${type}">${message}</div>`; setTimeout(() => { this.messageDiv.innerHTML = ''; }, 5000); } updateOrderCount() { if (this.orderCountSpan) { this.orderCountSpan.textContent = this.ordersData.length; } } async loadOrder() { const orderId = this.orderIdInput.value.trim(); const apiEndpoint = this.apiEndpointInput.value.trim(); if (!orderId) { this.showMessage('Please enter an order ID', 'error'); return; } // Check if order already exists const existingOrder = this.ordersData.find(order => order.order.id_orders === orderId); if (existingOrder) { this.showMessage(`Order ${orderId} already loaded`, 'warning'); return; } this.loadOrderBtn.disabled = true; this.showMessage('Loading order data...', 'info'); try { // For demo purposes, we'll simulate the API response const response = await fetch(`${apiEndpoint}${orderId}`, { method: 'GET', headers: { 'Content-Type': 'application/json', } }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const result = await response.json(); let orderData; if (result.success && result.data) { orderData = result.data; } else if (result.website_data) { orderData = result; } else { throw new Error('Invalid response format'); } // Add the new order to the array this.ordersData.push(orderData); this.renderPreview(); this.enablePrintButtons(); this.updateOrderCount(); this.showMessage(`Pesanan ${orderId} berhasil ditambahin! Total pesanan: ${this.ordersData.length}`, 'success'); // Clear the input field for next order this.orderIdInput.value = ''; } catch (error) { // If API fails, use mock data for demo this.showMessage(`API failed, using mock data for order ${orderId}`, 'warning'); const mockData = this.getMockOrderData(orderId); this.ordersData.push(mockData); this.renderPreview(); this.enablePrintButtons(); this.updateOrderCount(); this.orderIdInput.value = ''; } finally { this.loadOrderBtn.disabled = false; } } maskName(name) { if (!name || name.length < 2) return name; return name[0] + '*'.repeat(name.length - 1); } maskPhone(phone) { if (!phone || phone.length < 4) return phone; return phone.slice(0, 2) + '*'.repeat(phone.length - 4) + phone.slice(-2); } getMockOrderData(orderId) { // Mock data with different customer info for each order const mockCustomers = [{ name: 'John Doe', email: 'john.doe@example.com', phone: '(555) 987-6543', address: '456 Customer Ave', city: 'Customer City', state: 'CS', zip: '54321' }, { name: 'Jane Smith', email: 'jane.smith@example.com', phone: '(555) 123-7890', address: '789 Buyer Street', city: 'Shopping Town', state: 'ST', zip: '67890' }, { name: 'Bob Johnson', email: 'bob.johnson@example.com', phone: '(555) 456-1234', address: '321 Purchase Blvd', city: 'Order City', state: 'OC', zip: '13579' } ]; const mockProducts = [ [{ product_name: 'Wireless Headphones', quantity: 1, price: 199.99, sku: 'WH-001' }, { product_name: 'Phone Case', quantity: 2, price: 49.99, sku: 'PC-002' } ], [{ product_name: 'Bluetooth Speaker', quantity: 1, price: 89.99, sku: 'BS-003' }, { product_name: 'USB Cable', quantity: 3, price: 15.99, sku: 'UC-004' } ], [{ product_name: 'Laptop Stand', quantity: 1, price: 59.99, sku: 'LS-005' }, { product_name: 'Wireless Mouse', quantity: 1, price: 29.99, sku: 'WM-006' } ] ]; const customerIndex = (parseInt(orderId) % mockCustomers.length); const productIndex = (parseInt(orderId) % mockProducts.length); return { website_data: { logo: 'Your Store Logo', website_name: 'Your eCommerce Store', address: '123 Business St, City, State 12345', phone: '(555) 123-4567' }, order: { id_orders: orderId, order_date: '2024-07-14', total_amount: mockProducts[productIndex].reduce((sum, item) => sum + (item.price * item.quantity), 0), shipping_method: 'Standard Shipping' }, customer: mockCustomers[customerIndex], orders_detail: mockProducts[productIndex] }; } renderPreview() { const labelsHtml = this.ordersData.map(orderData => this.generateLabelHtml(orderData)).join(''); this.previewContent.innerHTML = ` <div class="labels-grid"> ${labelsHtml} </div> `; } generateLabelHtml(data) { const baseUrl = "<?= base_url() ?>"; const itemsHtml = data.orders_detail.map(item => ` <div class="item"> <span>${item.product_name} (${item.sku})</span> <span>x ${item.quantity}</span> </div> `).join(''); return ` <div class="label" data-order-id="${data.order.id_orders}"> <div class="label-header"> <div class="logo"> <img src="${baseUrl}uploads/${data.website_data.logo}" alt="Logo" onerror="this.style.display='none'; this.nextElementSibling.style.display='block';"> <span style="display: none;">${data.website_data.logo}</span> </div> <div class="order-info">Order: ${data.order.id_orders}</div> <button class="remove-label-btn" onclick="shippingPrinter.removeOrder('${data.order.id_orders}')" title="Remove this order">✕</button> </div> <div class="shipping-info"> <div class="shipping-pair"> <span class="shipping-label">Layanan:</span> <span class="shipping-value">${data.order.shipment_type}</span> </div> <div class="shipping-pair"> <span class="shipping-label">Kurir:</span> <span class="shipping-value">${data.order.shipment_name}</span> </div> <div class="shipping-pair"> <span class="shipping-label">Ongkir:</span> <span class="shipping-value">IDR ${data.order.shipping_fee}</span> </div> </div> <div class="address-section"> <div class="address-title">Pengirim:</div> <div class="address-content"> ${this.maskName(data.website_data.website_name)}<br> Tebet, Jakarta Selatan, Indonesia<br> </div> </div> <div class="address-section"> <div class="address-title">Penerima:</div> <div class="address-content"> ${data.customer.name} (${this.maskPhone(data.customer.phone)})<br> ${data.customer.address}<br> ${data.customer.subdistrict}, ${data.customer.district}, ${data.customer.province}<br> </div> </div> <div class="cut-line"></div> <div class="items-list"> <div style="font-weight: bold; margin-bottom: 3px;">Items:</div> ${itemsHtml} </div> </div> `; } removeOrder(orderId) { this.ordersData = this.ordersData.filter(order => order.order.id_orders !== orderId); this.renderPreview(); this.updateOrderCount(); this.showMessage(`Order ${orderId} removed`, 'info'); if (this.ordersData.length === 0) { this.printLabelBtn.disabled = true; // this.generatePDFBtn.disabled = true; } } clearAllOrders() { this.ordersData = []; this.renderPreview(); this.updateOrderCount(); this.printLabelBtn.disabled = true; // this.generatePDFBtn.disabled = true; this.showMessage('All orders cleared', 'info'); } enablePrintButtons() { this.printLabelBtn.disabled = false; // this.generatePDFBtn.disabled = false; } printLabels() { if (this.ordersData.length === 0) { this.showMessage('Please load at least one order first', 'error'); return; } const baseUrl = "<?= base_url() ?>"; // Create a new window for printing const printWindow = window.open('', '_blank'); const labelsHtml = this.ordersData.map(orderData => { const itemsHtml = orderData.orders_detail.map(item => ` <div class="item"> <span>${item.product_name} (${item.sku})</span> <span>x ${item.quantity}</span> </div> `).join(''); return ` <div class="label" data-order-id="${orderData.order.id_orders}"> <div class="label-header"> <div class="logo"> <img src="${baseUrl}uploads/${orderData.website_data.logo}" alt="Logo" onerror="this.style.display='none'; this.nextElementSibling.style.display='block';"> <span style="display: none;">${orderData.website_data.logo}</span> </div> <div class="order-info">Order: ${orderData.order.id_orders}</div> </div> <div class="shipping-info"> <div class="shipping-pair"> <span class="shipping-label">Layanan:</span> <span class="shipping-value">${orderData.order.shipment_type}</span> </div> <div class="shipping-pair"> <span class="shipping-label">Kurir:</span> <span class="shipping-value">${orderData.order.shipment_name}</span> </div> <div class="shipping-pair"> <span class="shipping-label">Ongkir:</span> <span class="shipping-value">IDR ${orderData.order.shipping_fee}</span> </div> </div> <div class="address-section"> <div class="address-title">Pengirim:</div> <div class="address-content"> ${this.maskName(orderData.website_data.website_name)}<br> Tebet, Jakarta Selatan, Indonesia<br> </div> </div> <div class="address-section"> <div class="address-title">Penerima:</div> <div class="address-content"> ${orderData.customer.name} (${this.maskPhone(orderData.customer.phone)})<br> ${orderData.customer.address}<br> ${orderData.customer.subdistrict}, ${orderData.customer.district}, ${orderData.customer.province}<br> </div> </div> <div class="cut-line"></div> <div class="items-list"> <div style="font-weight: bold; margin-bottom: 3px;">Items:</div> ${itemsHtml} </div> </div> `; }).join(''); printWindow.document.write(` <!DOCTYPE html> <html> <head> <title>Shipping Labels - ${this.ordersData.length} Orders</title> <style> @page { size: A4; margin: 0.5in; } body { font-family: Arial, sans-serif; margin: 0; padding: 0; } .labels-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin: 10px; } .label { border: 2px solid #000; padding: 8px; font-size: 12px; position: relative; background: white; break-inside: avoid; page-break-inside: avoid; height: 350px; } .label-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px; padding-bottom: 10px; border-bottom: 1px solid #333; } .logo { display: flex; align-items: center; height: 30px; } .logo img { max-height: 30px; max-width: 120px; object-fit: contain; } .logo span { font-weight: bold; font-size: 12px; color: #007bff; } .order-info { font-size: 9px; color: white; } .address-section { margin-bottom: 8px; } .shipping-info { display: flex; justify-content: space-between; flex-wrap: wrap; margin-bottom: 10px; padding-bottom: 8px; border-bottom: 1px solid #ddd; } .shipping-pair { display: flex; flex-direction: column; min-width: 80px; text-align: left; } .shipping-pair .shipping-label { font-weight: bold; color: #333; font-size: 9px; margin-bottom: 2px; } .shipping-pair .shipping-value { color: #444; font-size: 8px; } .address-title { font-weight: bold; font-size: 9px; color: #333; margin-bottom: 2px; } .address-content { font-size: 8px; line-height: 1.2; color: #555; } .remove-label-btn { display: none; /* Hide remove button in print */ } .cut-line { border-top: 2px dashed #333; margin: 15px 0; position: relative; } .cut-line::before { content: "✂ Cut here"; position: absolute; right: 0; top: -10px; font-size: 10px; background: white; padding: 0 5px; } .items-list { font-size: 7px; line-height: 1.2; } .item { margin-bottom: 3px; display: flex; justify-content: space-between; } </style> </head> <body> <div class="labels-grid"> ${labelsHtml} </div> </body> </html> `); printWindow.document.close(); printWindow.focus(); // Print after a short delay to ensure content is loaded setTimeout(() => { printWindow.print(); printWindow.close(); }, 500); } generatePDF() { if (this.ordersData.length === 0) { this.showMessage('Please load at least one order first', 'error'); return; } const { jsPDF } = window.jspdf; const doc = new jsPDF(); const pageWidth = doc.internal.pageSize.width; const pageHeight = doc.internal.pageSize.height; // Calculate label dimensions (1/4 of A4) const labelWidth = (pageWidth - 30) / 2; // 2 columns with margins const labelHeight = (pageHeight - 30) / 2; // 2 rows with margins const positions = [{ x: 15, y: 15 }, // Top-left { x: 15 + labelWidth + 10, y: 15 }, // Top-right { x: 15, y: 15 + labelHeight + 10 }, // Bottom-left { x: 15 + labelWidth + 10, y: 15 + labelHeight + 10 } // Bottom-right ]; // Generate labels for each order this.ordersData.forEach((orderData, orderIndex) => { const pageNumber = Math.floor(orderIndex / 4); const positionIndex = orderIndex % 4; // Add new page if needed if (pageNumber > 0 && positionIndex === 0) { doc.addPage(); } const pos = positions[positionIndex]; this.drawLabel(doc, pos.x, pos.y, labelWidth, labelHeight, orderData); }); // Save the PDF const orderIds = this.ordersData.map(order => order.order.id_orders).join('-'); doc.save(`shipping-labels-orders-${orderIds}.pdf`); this.showMessage('PDF generated successfully!', 'success'); } drawLabel(doc, x, y, width, height, data) { // Draw border doc.rect(x, y, width, height); let currentY = y + 10; // Header doc.setFontSize(12); doc.setFont(undefined, 'bold'); // Try to add logo image, fallback to text if fails try { const baseUrl = "<?= base_url() ?>"; const logoUrl = `${baseUrl}uploads/${data.website_data.logo}`; // For PDF, we'll just use text for now since adding images requires more complex handling doc.text(data.website_data.website_name, x + 5, currentY); } catch (error) { doc.text(data.website_data.website_name, x + 5, currentY); } doc.text(`Order: ${data.order.id_orders}`, x + width - 40, currentY); currentY += 15; // FROM section doc.setFontSize(10); doc.setFont(undefined, 'bold'); doc.text('FROM:', x + 5, currentY); currentY += 5; doc.setFont(undefined, 'normal'); doc.setFontSize(9); doc.text(data.website_data.website_name, x + 5, currentY); currentY += 4; doc.text(data.website_data.address, x + 5, currentY); currentY += 4; doc.text(data.website_data.phone, x + 5, currentY); currentY += 10; // TO section doc.setFontSize(10); doc.setFont(undefined, 'bold'); doc.text('TO:', x + 5, currentY); currentY += 5; doc.setFont(undefined, 'normal'); doc.setFontSize(9); doc.text(data.customer.name, x + 5, currentY); currentY += 4; doc.text(data.customer.address, x + 5, currentY); currentY += 4; doc.text(`${data.customer.city}, ${data.customer.state} ${data.customer.zip}`, x + 5, currentY); currentY += 4; doc.text(data.customer.phone, x + 5, currentY); currentY += 10; // Dashed line doc.setLineDashPattern([2, 2], 0); doc.line(x + 5, currentY, x + width - 5, currentY); doc.setLineDashPattern([], 0); // Cut here text doc.setFontSize(8); doc.text('✂ Cut here', x + width - 25, currentY - 2); currentY += 8; // Items section doc.setFontSize(9); doc.setFont(undefined, 'bold'); doc.text('Items:', x + 5, currentY); currentY += 5; doc.setFont(undefined, 'normal'); doc.setFontSize(8); data.orders_detail.forEach(item => { const itemText = `${item.product_name} (${item.sku})`; const qtyText = `Qty: ${item.quantity}`; doc.text(itemText, x + 5, currentY); doc.text(qtyText, x + width - 25, currentY); currentY += 4; }); } } // Initialize the application let shippingPrinter; document.addEventListener('DOMContentLoaded', () => { shippingPrinter = new ShippingLabelPrinter(); }); </script> </body> </html>