1133 lines
53 KiB
HTML
1133 lines
53 KiB
HTML
{% extends "base.html" %}
|
||
|
||
{% block head %}
|
||
<style>
|
||
#label-preview {
|
||
background: #fafafa;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
/* Inserted custom CSS from user */
|
||
.card.scan-table-card table.print-module-table.scan-table thead th {
|
||
border-bottom: 2px solid #dee2e6 !important;
|
||
background-color: #f8f9fa !important;
|
||
padding: 0.25rem 0.4rem !important;
|
||
text-align: left !important;
|
||
font-weight: 600 !important;
|
||
font-size: 10px !important;
|
||
line-height: 1.2 !important;
|
||
}
|
||
|
||
/* Enhanced table styling */
|
||
.card.scan-table-card table.print-module-table.scan-table {
|
||
width: 100% !important;
|
||
border-collapse: collapse !important;
|
||
}
|
||
|
||
.card.scan-table-card table.print-module-table.scan-table tbody tr:hover td {
|
||
background-color: #f8f9fa !important;
|
||
cursor: pointer !important;
|
||
}
|
||
|
||
.card.scan-table-card table.print-module-table.scan-table tbody tr.selected td {
|
||
background-color: #007bff !important;
|
||
color: white !important;
|
||
}
|
||
</style>
|
||
{% endblock %}
|
||
|
||
{% block content %}
|
||
<div class="scan-container" style="display: flex; flex-direction: row; gap: 20px; width: 100%; align-items: flex-start;">
|
||
<!-- Label Preview Card -->
|
||
<div class="card scan-form-card" style="display: flex; flex-direction: column; justify-content: flex-start; align-items: center; min-height: 700px; width: 330px; flex-shrink: 0; position: relative; padding: 15px;">
|
||
<div class="label-view-title" style="width: 100%; text-align: center; padding: 0 0 15px 0; font-size: 18px; font-weight: bold; letter-spacing: 0.5px;">Label View</div>
|
||
|
||
<!-- Label Preview Section -->
|
||
<div id="label-preview" style="border: 1px solid #ddd; padding: 10px; position: relative; background: #fafafa; width: 301px; height: 434.7px;">
|
||
<!-- Label content rectangle -->
|
||
<div id="label-content" style="position: absolute; top: 65.7px; left: 11.34px; width: 227.4px; height: 321.3px; border: 2px solid #333; background: white;">
|
||
<!-- Top row content: Company name -->
|
||
<div style="position: absolute; top: 0; left: 0; right: 0; height: 32.13px; display: flex; align-items: center; justify-content: center; font-weight: bold; font-size: 12px; color: #000; z-index: 10;">
|
||
INNOFA RROMANIA SRL
|
||
</div>
|
||
|
||
<!-- Row 2 content: Customer Name -->
|
||
<div id="customer-name-row" style="position: absolute; top: 32.13px; left: 0; right: 0; height: 32.13px; display: flex; align-items: center; justify-content: center; font-size: 11px; color: #000;">
|
||
<!-- Customer name will be populated here -->
|
||
</div>
|
||
|
||
<!-- Horizontal dividing lines for 9 rows (row 6 is double height) -->
|
||
<div style="position: absolute; top: 32.13px; left: 0; right: 0; height: 1px; background: #999;"></div>
|
||
<div style="position: absolute; top: 64.26px; left: 0; right: 0; height: 1px; background: #999;"></div>
|
||
<div style="position: absolute; top: 96.39px; left: 0; right: 0; height: 1px; background: #999;"></div>
|
||
<div style="position: absolute; top: 128.52px; left: 0; right: 0; height: 1px; background: #999;"></div>
|
||
<div style="position: absolute; top: 160.65px; left: 0; right: 0; height: 1px; background: #999;"></div>
|
||
<!-- Row 6 is double height for Description -->
|
||
<div style="position: absolute; top: 224.91px; left: 0; right: 0; height: 1px; background: #999;"></div>
|
||
<!-- Row 7 for Size -->
|
||
<div style="position: absolute; top: 257.04px; left: 0; right: 0; height: 1px; background: #999;"></div>
|
||
<!-- Row 8 for Article Code -->
|
||
<div style="position: absolute; top: 289.17px; left: 0; right: 0; height: 1px; background: #999;"></div>
|
||
|
||
<!-- Vertical dividing line starting from row 3 to bottom at 40% width -->
|
||
<div style="position: absolute; left: 90.96px; top: 64.26px; width: 1px; height: 257.04px; background: #999;"></div>
|
||
|
||
<!-- Row 3 content: Quantity ordered -->
|
||
<div style="position: absolute; top: 64.26px; left: 0; width: 90.96px; height: 32.13px; display: flex; align-items: center; padding-left: 5px; font-size: 10px; color: #000;">
|
||
Quantity ordered
|
||
</div>
|
||
<div id="quantity-ordered-value" style="position: absolute; top: 64.26px; left: 90.96px; width: 136.44px; height: 32.13px; display: flex; align-items: center; justify-content: center; font-size: 13px; font-weight: bold; color: #000;">
|
||
<!-- Quantity value will be populated here -->
|
||
</div>
|
||
|
||
<!-- Row 4 content: Customer order -->
|
||
<div style="position: absolute; top: 96.39px; left: 0; width: 90.96px; height: 32.13px; display: flex; align-items: center; padding-left: 5px; font-size: 10px; color: #000;">
|
||
Customer order
|
||
</div>
|
||
<div id="client-order-info" style="position: absolute; top: 96.39px; left: 90.96px; width: 136.44px; height: 32.13px; display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: bold; color: #000;">
|
||
<!-- Client order info will be populated here -->
|
||
</div>
|
||
|
||
<!-- Row 5 content: Delivery date -->
|
||
<div style="position: absolute; top: 128.52px; left: 0; width: 90.96px; height: 32.13px; display: flex; align-items: center; padding-left: 5px; font-size: 10px; color: #000;">
|
||
Delivery date
|
||
</div>
|
||
<div id="delivery-date-value" style="position: absolute; top: 128.52px; left: 90.96px; width: 136.44px; height: 32.13px; display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: bold; color: #000;">
|
||
<!-- Delivery date value will be populated here -->
|
||
</div>
|
||
|
||
<!-- Row 6 content: Description (double height row) -->
|
||
<div style="position: absolute; top: 160.65px; left: 0; width: 90.96px; height: 64.26px; display: flex; align-items: center; padding-left: 5px; font-size: 10px; color: #000;">
|
||
Description
|
||
</div>
|
||
<div id="description-value" style="position: absolute; top: 160.65px; left: 90.96px; width: 136.44px; height: 64.26px; display: flex; align-items: center; justify-content: center; font-size: 11px; font-weight: bold; color: #000; text-align: center; line-height: 1.2; padding: 2px; overflow: hidden; word-wrap: break-word;">
|
||
<!-- Description value will be populated here -->
|
||
</div>
|
||
|
||
<!-- Row 7 content: Size -->
|
||
<div style="position: absolute; top: 224.91px; left: 0; width: 90.96px; height: 32.13px; display: flex; align-items: center; padding-left: 5px; font-size: 10px; color: #000;">
|
||
Size
|
||
</div>
|
||
<div id="size-value" style="position: absolute; top: 224.91px; left: 90.96px; width: 136.44px; height: 32.13px; display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: bold; color: #000;">
|
||
<!-- Size value will be populated here -->
|
||
</div>
|
||
|
||
<!-- Row 8 content: Article Code -->
|
||
<div style="position: absolute; top: 257.04px; left: 0; width: 90.96px; height: 32.13px; display: flex; align-items: center; padding-left: 5px; font-size: 10px; color: #000;">
|
||
Article Code
|
||
</div>
|
||
<div id="article-code-value" style="position: absolute; top: 257.04px; left: 90.96px; width: 136.44px; height: 32.13px; display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: bold; color: #000;">
|
||
<!-- Article code value will be populated here -->
|
||
</div>
|
||
|
||
<!-- Row 9 content: Prod Order (final row) -->
|
||
<div style="position: absolute; top: 289.17px; left: 0; width: 90.96px; height: 32.13px; display: flex; align-items: center; padding-left: 5px; font-size: 10px; color: #000;">
|
||
Prod Order
|
||
</div>
|
||
<div id="prod-order-value" style="position: absolute; top: 289.17px; left: 90.96px; width: 136.44px; height: 32.13px; display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: bold; color: #000;">
|
||
<!-- Prod order value will be populated here -->
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Barcode Frame - positioned 10px below rectangle with 2mm side margins -->
|
||
<div id="barcode-frame" style="position: absolute; top: 395px; left: 7.56px; width: 295.44px; height: 50px; background: white; display: flex; flex-direction: column; align-items: center; justify-content: center;">
|
||
<!-- Code 128 Barcode representation -->
|
||
<div id="barcode-display" style="width: 100%; height: 45px; background: linear-gradient(90deg, #000 1px, transparent 1px, transparent 2px, #000 2px, transparent 3px, #000 4px, transparent 5px, #000 6px, transparent 7px, #000 8px, transparent 9px, #000 10px, transparent 11px, #000 12px, transparent 13px); background-size: 15px 100%; background-repeat: repeat-x;">
|
||
</div>
|
||
<!-- Barcode text below the bars -->
|
||
<div id="barcode-text" style="font-size: 8px; font-family: 'Courier New', monospace; margin-top: 2px; text-align: center; font-weight: bold;">
|
||
<!-- Barcode text will be populated here -->
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Vertical Barcode Frame - positioned on the right side of the label -->
|
||
<div id="vertical-barcode-frame" style="position: absolute; top: 70px; left: 245px; width: 50px; height: 309.96px; background: white; display: flex; flex-direction: column; align-items: center; justify-content: center;">
|
||
<!-- Vertical Code 128 Barcode representation -->
|
||
<div id="vertical-barcode-display" style="width: 50px; height: 280px; background: linear-gradient(0deg, #000 1px, transparent 1px, transparent 2px, #000 2px, transparent 3px, #000 4px, transparent 5px, #000 6px, transparent 7px, #000 8px, transparent 9px, #000 10px, transparent 11px, #000 12px, transparent 13px); background-size: 100% 15px; background-repeat: repeat-y;">
|
||
</div>
|
||
<!-- Vertical barcode text -->
|
||
<div id="vertical-barcode-text" style="font-size: 6px; font-family: 'Courier New', monospace; margin-top: 5px; text-align: center; font-weight: bold; writing-mode: vertical-rl; text-orientation: mixed;">
|
||
<!-- Vertical barcode text will be populated here -->
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Print Options and Button Section - Inside the Card -->
|
||
<div style="width: 100%; margin-top: 20px;">
|
||
<!-- Print Method Selection -->
|
||
<div style="margin-bottom: 15px;">
|
||
<label style="font-size: 12px; font-weight: 600; color: #495057; margin-bottom: 8px; display: block;">
|
||
🖨️ Print Method:
|
||
</label>
|
||
|
||
<div class="form-check mb-2">
|
||
<input class="form-check-input" type="radio" name="printMethod" id="directPrint" value="direct" checked>
|
||
<label class="form-check-label" for="directPrint" style="font-size: 11px; line-height: 1.3;">
|
||
<strong>Direct Print</strong><br>
|
||
<span class="text-muted">Print directly to thermal label printer</span>
|
||
</label>
|
||
</div>
|
||
|
||
<div class="form-check mb-2">
|
||
<input class="form-check-input" type="radio" name="printMethod" id="pdfGenerate" value="pdf">
|
||
<label class="form-check-label" for="pdfGenerate" style="font-size: 11px; line-height: 1.3;">
|
||
<strong>Generate PDF</strong><br>
|
||
<span class="text-muted">Create PDF for manual printing</span>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Printer Selection -->
|
||
<div id="printerSelection" style="margin-bottom: 15px;">
|
||
<label style="font-size: 12px; font-weight: 600; color: #495057; margin-bottom: 5px; display: block;">
|
||
Select Printer:
|
||
</label>
|
||
<select id="printerSelect" class="form-control form-control-sm" style="font-size: 11px; padding: 4px 8px;">
|
||
<option value="default">Default Printer</option>
|
||
<option value="Epson TM-T20">Epson TM-T20</option>
|
||
<option value="Citizen CTS-310">Citizen CTS-310</option>
|
||
<option value="custom">Other Printer...</option>
|
||
</select>
|
||
<small class="text-muted" style="font-size: 10px;">Choose your thermal label printer</small>
|
||
</div>
|
||
|
||
<!-- Print Button -->
|
||
<div style="width: 100%; text-align: center; margin-bottom: 15px;">
|
||
<button id="print-label-btn" class="btn btn-success" style="font-size: 14px; padding: 10px 30px; border-radius: 6px; font-weight: 600;">
|
||
🖨️ Print Labels
|
||
</button>
|
||
</div>
|
||
|
||
<!-- Print Information -->
|
||
<div style="width: 100%; text-align: center; color: #6c757d; font-size: 11px; line-height: 1.4;">
|
||
<div style="margin-bottom: 5px;">Creates sequential labels based on quantity</div>
|
||
<small>(e.g., CP00000711-001 to CP00000711-063)</small>
|
||
</div>
|
||
|
||
<!-- Direct Print Service Download -->
|
||
<div style="width: 100%; text-align: center; margin-top: 20px; padding-top: 15px; border-top: 1px solid #e9ecef;">
|
||
<!-- Electron App (Recommended) -->
|
||
<div style="background: #d4edda; border: 1px solid #c3e6cb; border-radius: 6px; padding: 12px; margin-bottom: 10px;">
|
||
<div style="font-size: 11px; color: #155724; margin-bottom: 8px;">
|
||
<strong>🖥️ Quality Print Desktop v1.0.0 (RECOMMENDED)</strong>
|
||
</div>
|
||
<div style="font-size: 10px; color: #495057; margin-bottom: 10px; line-height: 1.3;">
|
||
Professional desktop app • Direct hardware access • Rich formatting
|
||
</div>
|
||
<button onclick="downloadElectronApp()" class="btn btn-success btn-sm" style="font-size: 10px; padding: 4px 12px; margin-right: 5px;">
|
||
📥 Download Linux Version (99MB)
|
||
</button>
|
||
<button onclick="downloadPortableApp()" class="btn btn-outline-success btn-sm" style="font-size: 10px; padding: 4px 12px; margin-right: 5px;">
|
||
💻 Windows Portable (8KB)
|
||
</button>
|
||
<small style="color: #28a745; font-weight: bold;">✅ READY!</small>
|
||
</div>
|
||
|
||
<!-- Windows Service (Deprecated) -->
|
||
<div style="background: #f8d7da; border: 1px solid #f5c6cb; border-radius: 6px; padding: 12px; margin-bottom: 10px;">
|
||
<div style="font-size: 11px; color: #721c24; margin-bottom: 8px;">
|
||
<strong>📦 Windows Print Service (DEPRECATED)</strong>
|
||
</div>
|
||
<div style="font-size: 10px; color: #6c757d; margin-bottom: 10px; line-height: 1.3;">
|
||
Legacy background service • Limited features • Use desktop app instead
|
||
</div>
|
||
<a href="{{ url_for('main.download_print_service') }}?package=complete" class="btn btn-outline-secondary btn-sm" download style="font-size: 10px; padding: 4px 12px; text-decoration: none;">
|
||
📥 Download Legacy Service
|
||
</a>
|
||
</div>
|
||
|
||
<div style="font-size: 9px; color: #6c757d; margin-top: 5px; line-height: 1.2;">
|
||
<strong>🏆 Desktop App Benefits:</strong> Direct thermal printing • Rich barcodes/QR codes • Better reliability<br>
|
||
<small>✨ Two versions: Full Linux AppImage or Lightweight Windows Portable • Cross-platform support</small>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div> <!-- Data Preview Card -->
|
||
<div class="card scan-table-card" style="min-height: 700px; width: calc(100% - 350px); margin: 0;">
|
||
<h3>Data Preview (Unprinted Orders)</h3>
|
||
<button id="check-db-btn" class="btn btn-primary mb-3">Check Database</button>
|
||
<div class="report-table-container">
|
||
<table class="scan-table print-module-table">
|
||
<thead>
|
||
<tr>
|
||
<th>ID</th>
|
||
<th>Comanda Productie</th>
|
||
<th>Cod Articol</th>
|
||
<th>Descr. Com. Prod</th>
|
||
<th>Cantitate</th>
|
||
<th>Data Livrare</th>
|
||
<th>Dimensiune</th>
|
||
<th>Com. Achiz. Client</th>
|
||
<th>Nr. Linie</th>
|
||
<th>Customer Name</th>
|
||
<th>Customer Art. Nr.</th>
|
||
<th>Open Order</th>
|
||
<th>Line</th>
|
||
<th>Printed</th>
|
||
<th>Created</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="unprinted-orders-table">
|
||
<!-- Data will be dynamically loaded here -->
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
document.getElementById('check-db-btn').addEventListener('click', function() {
|
||
const button = this;
|
||
button.textContent = 'Loading...';
|
||
button.disabled = true;
|
||
|
||
fetch('/get_unprinted_orders')
|
||
.then(response => {
|
||
if (response.status === 403) {
|
||
return response.json().then(errorData => {
|
||
throw new Error(`Access Denied: ${errorData.error}`);
|
||
});
|
||
} else if (!response.ok) {
|
||
return response.text().then(text => {
|
||
throw new Error(`HTTP ${response.status}: ${text}`);
|
||
});
|
||
}
|
||
return response.json();
|
||
})
|
||
.then(data => {
|
||
console.log('Received data:', data);
|
||
const tbody = document.getElementById('unprinted-orders-table');
|
||
tbody.innerHTML = '';
|
||
data.forEach((order, index) => {
|
||
const tr = document.createElement('tr');
|
||
tr.dataset.orderId = order.id;
|
||
tr.dataset.orderIndex = index;
|
||
tr.style.cursor = 'pointer';
|
||
tr.innerHTML = `
|
||
<td style="font-size: 9px;">${order.id}</td>
|
||
<td style="font-size: 9px;"><strong>${order.comanda_productie}</strong></td>
|
||
<td style="font-size: 9px;">${order.cod_articol || '-'}</td>
|
||
<td style="font-size: 9px;">${order.descr_com_prod}</td>
|
||
<td style="text-align: right; font-weight: 600; font-size: 9px;">${order.cantitate}</td>
|
||
<td style="text-align: center; font-size: 9px;">
|
||
${order.data_livrare ? new Date(order.data_livrare).toLocaleDateString() : '-'}
|
||
</td>
|
||
<td style="text-align: center; font-size: 9px;">${order.dimensiune || '-'}</td>
|
||
<td style="font-size: 9px;">${order.com_achiz_client || '-'}</td>
|
||
<td style="text-align: right; font-size: 9px;">${order.nr_linie_com_client || '-'}</td>
|
||
<td style="font-size: 9px;">${order.customer_name || '-'}</td>
|
||
<td style="font-size: 9px;">${order.customer_article_number || '-'}</td>
|
||
<td style="font-size: 9px;">${order.open_for_order || '-'}</td>
|
||
<td style="text-align: right; font-size: 9px;">${order.line_number || '-'}</td>
|
||
<td style="text-align: center; font-size: 9px;">
|
||
${order.printed_labels == 1 ?
|
||
'<span style="color: #28a745; font-weight: bold;">✓ Yes</span>' :
|
||
'<span style="color: #dc3545;">✗ No</span>'}
|
||
</td>
|
||
<td style="font-size: 9px; color: #6c757d;">
|
||
${order.created_at ? new Date(order.created_at).toLocaleString() : '-'}
|
||
</td>
|
||
`;
|
||
|
||
// Add click event for row selection
|
||
tr.addEventListener('click', function() {
|
||
console.log('Row clicked:', order.id);
|
||
|
||
// Remove selection from other rows
|
||
document.querySelectorAll('.print-module-table tbody tr').forEach(row => {
|
||
row.classList.remove('selected');
|
||
const cells = row.querySelectorAll('td');
|
||
cells.forEach(cell => {
|
||
cell.style.backgroundColor = '';
|
||
cell.style.color = '';
|
||
});
|
||
});
|
||
|
||
// Select this row
|
||
this.classList.add('selected');
|
||
|
||
// Force visual selection with inline styles
|
||
const cells = this.querySelectorAll('td');
|
||
cells.forEach(cell => {
|
||
cell.style.backgroundColor = '#007bff';
|
||
cell.style.color = 'white';
|
||
});
|
||
|
||
// Update label preview with selected order data
|
||
updateLabelPreview(order);
|
||
});
|
||
|
||
tbody.appendChild(tr);
|
||
});
|
||
|
||
// Update the label preview with first row by default
|
||
if (data.length > 0) {
|
||
updateLabelPreview(data[0]);
|
||
|
||
// Initialize PDF generation functionality
|
||
addPDFGenerationHandler();
|
||
|
||
// Initialize print service
|
||
initializePrintService();
|
||
|
||
// Auto-select first row
|
||
setTimeout(() => {
|
||
const firstRow = document.querySelector('.print-module-table tbody tr');
|
||
if (firstRow) {
|
||
firstRow.classList.add('selected');
|
||
const cells = firstRow.querySelectorAll('td');
|
||
cells.forEach(cell => {
|
||
cell.style.backgroundColor = '#007bff';
|
||
cell.style.color = 'white';
|
||
});
|
||
}
|
||
}, 100);
|
||
}
|
||
})
|
||
.catch(error => console.error('Error fetching orders:', error))
|
||
.finally(() => {
|
||
button.textContent = 'Check Database';
|
||
button.disabled = false;
|
||
});
|
||
});
|
||
|
||
// Function to update label preview with selected order data
|
||
function updateLabelPreview(order) {
|
||
const customerName = order.customer_name || 'N/A';
|
||
document.getElementById('customer-name-row').textContent = customerName;
|
||
|
||
// Update quantity ordered value
|
||
const quantity = order.cantitate || '0';
|
||
document.getElementById('quantity-ordered-value').textContent = quantity;
|
||
|
||
// Update client order info (Com.Achiz.Client - Nr. Linie)
|
||
const comAchizClient = order.com_achiz_client || '';
|
||
const nrLinie = order.nr_linie_com_client || '';
|
||
const clientOrderInfo = comAchizClient && nrLinie ? `${comAchizClient}-${nrLinie}` : 'N/A';
|
||
document.getElementById('client-order-info').textContent = clientOrderInfo;
|
||
|
||
// Update vertical barcode with client order info or default
|
||
const verticalBarcodeData = clientOrderInfo !== 'N/A' ? clientOrderInfo : '000000-00';
|
||
document.getElementById('vertical-barcode-text').textContent = verticalBarcodeData;
|
||
|
||
// Update delivery date (using data_livrare column)
|
||
const deliveryDate = order.data_livrare ? new Date(order.data_livrare).toLocaleDateString() : 'N/A';
|
||
document.getElementById('delivery-date-value').textContent = deliveryDate;
|
||
|
||
// Update size (using dimensiune column)
|
||
const size = order.dimensiune || 'N/A';
|
||
document.getElementById('size-value').textContent = size;
|
||
|
||
// Update description (Descr. Com. Prod)
|
||
const description = order.descr_com_prod || 'N/A';
|
||
document.getElementById('description-value').textContent = description;
|
||
|
||
// Update article code (using customer_article_number column)
|
||
const articleCode = order.customer_article_number || 'N/A';
|
||
document.getElementById('article-code-value').textContent = articleCode;
|
||
|
||
// Update prod order (comanda_productie - cantitate)
|
||
const comandaProductie = order.comanda_productie || '';
|
||
const cantitate = order.cantitate || '';
|
||
const prodOrder = comandaProductie && cantitate ? `${comandaProductie}-${cantitate}` : 'N/A';
|
||
document.getElementById('prod-order-value').textContent = prodOrder;
|
||
|
||
// Update barcode with the same prod order data
|
||
document.getElementById('barcode-text').textContent = prodOrder;
|
||
}
|
||
|
||
// Initialize print service connection and load available printers
|
||
function initializePrintService() {
|
||
// Check service status and load printers
|
||
checkPrintServiceStatus()
|
||
.then(serviceStatus => {
|
||
if (serviceStatus) {
|
||
console.log('✅ Print service is available');
|
||
loadAvailablePrinters();
|
||
updateServiceStatusIndicator(true);
|
||
} else {
|
||
console.log('⚠️ Print service not available');
|
||
updateServiceStatusIndicator(false);
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.log('❌ Print service connection failed:', error);
|
||
updateServiceStatusIndicator(false);
|
||
});
|
||
}
|
||
|
||
// Load available printers from print service
|
||
function loadAvailablePrinters() {
|
||
fetch('http://localhost:8899/printers', {
|
||
method: 'GET',
|
||
mode: 'cors'
|
||
})
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
const printerSelect = document.getElementById('printerSelect');
|
||
|
||
// Clear existing options except default ones
|
||
const defaultOptions = ['default', 'Epson TM-T20', 'Citizen CTS-310', 'custom'];
|
||
Array.from(printerSelect.options).forEach(option => {
|
||
if (!defaultOptions.includes(option.value)) {
|
||
option.remove();
|
||
}
|
||
});
|
||
|
||
// Add available printers
|
||
if (data.printers && data.printers.length > 0) {
|
||
data.printers.forEach(printer => {
|
||
// Don't add if already exists in default options
|
||
if (!defaultOptions.includes(printer.name)) {
|
||
const option = document.createElement('option');
|
||
option.value = printer.name;
|
||
option.textContent = `${printer.name}${printer.default ? ' (Default)' : ''}${printer.status !== 'ready' ? ' - Offline' : ''}`;
|
||
|
||
// Insert before "Other Printer..." option
|
||
const customOption = printerSelect.querySelector('option[value="custom"]');
|
||
printerSelect.insertBefore(option, customOption);
|
||
}
|
||
});
|
||
|
||
// Set default printer if available
|
||
const defaultPrinter = data.printers.find(p => p.default);
|
||
if (defaultPrinter) {
|
||
printerSelect.value = defaultPrinter.name;
|
||
}
|
||
}
|
||
|
||
console.log(`Loaded ${data.printers.length} printers from service`);
|
||
})
|
||
.catch(error => {
|
||
console.log('Could not load printers from service:', error);
|
||
});
|
||
}
|
||
|
||
// Update service status indicator
|
||
function updateServiceStatusIndicator(isAvailable) {
|
||
// Find or create status indicator
|
||
let statusIndicator = document.getElementById('service-status-indicator');
|
||
if (!statusIndicator) {
|
||
statusIndicator = document.createElement('div');
|
||
statusIndicator.id = 'service-status-indicator';
|
||
statusIndicator.style.cssText = 'font-size: 9px; color: #6c757d; margin-top: 5px; text-align: center;';
|
||
|
||
// Insert after printer selection
|
||
const printerSelection = document.getElementById('printerSelection');
|
||
printerSelection.appendChild(statusIndicator);
|
||
}
|
||
|
||
if (isAvailable) {
|
||
statusIndicator.innerHTML = '🟢 <strong>Print service connected</strong><br><small>Direct printing available</small>';
|
||
statusIndicator.style.color = '#28a745';
|
||
} else {
|
||
statusIndicator.innerHTML = '🔴 <strong>Print service offline</strong><br><small>Install and start the service for direct printing</small>';
|
||
statusIndicator.style.color = '#dc3545';
|
||
}
|
||
}
|
||
|
||
// PDF Generation System - No printer setup needed
|
||
// Labels are generated as PDF files for universal compatibility
|
||
function addPDFGenerationHandler() {
|
||
const printButton = document.getElementById('print-label-btn');
|
||
|
||
if (printButton) {
|
||
// Update button text and appearance based on print method
|
||
updatePrintButtonText();
|
||
|
||
// Add event listeners for radio buttons
|
||
document.querySelectorAll('input[name="printMethod"]').forEach(radio => {
|
||
radio.addEventListener('change', updatePrintButtonText);
|
||
});
|
||
|
||
printButton.addEventListener('click', function(e) {
|
||
e.preventDefault();
|
||
|
||
// Get selected order
|
||
const selectedRow = document.querySelector('.print-module-table tbody tr.selected');
|
||
if (!selectedRow) {
|
||
alert('Please select an order first from the table below.');
|
||
return;
|
||
}
|
||
|
||
// Get print method
|
||
const printMethod = document.querySelector('input[name="printMethod"]:checked').value;
|
||
|
||
if (printMethod === 'direct') {
|
||
handleDirectPrint(selectedRow);
|
||
} else {
|
||
handlePDFGeneration(selectedRow);
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
function updatePrintButtonText() {
|
||
const printButton = document.getElementById('print-label-btn');
|
||
const printMethod = document.querySelector('input[name="printMethod"]:checked').value;
|
||
const printerSelection = document.getElementById('printerSelection');
|
||
|
||
if (printMethod === 'direct') {
|
||
printButton.innerHTML = '🖨️ Print Directly';
|
||
printButton.title = 'Print directly to thermal label printer';
|
||
printerSelection.style.display = 'block';
|
||
} else {
|
||
printButton.innerHTML = '📄 Generate PDF';
|
||
printButton.title = 'Generate PDF with multiple labels based on quantity';
|
||
printerSelection.style.display = 'none';
|
||
}
|
||
}
|
||
|
||
function handleDirectPrint(selectedRow) {
|
||
const orderId = selectedRow.dataset.orderId;
|
||
const quantityCell = selectedRow.querySelector('td:nth-child(5)');
|
||
const quantity = quantityCell ? parseInt(quantityCell.textContent) : 1;
|
||
const prodOrderCell = selectedRow.querySelector('td:nth-child(2)');
|
||
const prodOrder = prodOrderCell ? prodOrderCell.textContent.trim() : 'N/A';
|
||
|
||
// Get selected printer
|
||
const printerSelect = document.getElementById('printerSelect');
|
||
let printerName = printerSelect.value;
|
||
|
||
if (printerName === 'custom') {
|
||
printerName = prompt('Enter your printer name:');
|
||
if (!printerName) return;
|
||
} else if (printerName === 'default') {
|
||
printerName = ''; // Empty for default printer
|
||
}
|
||
|
||
console.log(`Direct printing to: ${printerName || 'default printer'}`);
|
||
|
||
// Extract order data from selected row
|
||
const cells = selectedRow.querySelectorAll('td');
|
||
const orderData = {
|
||
id: parseInt(orderId),
|
||
comanda_productie: cells[1].textContent.trim(),
|
||
cod_articol: cells[2].textContent.trim(),
|
||
descr_com_prod: cells[3].textContent.trim(),
|
||
cantitate: parseInt(cells[4].textContent.trim()),
|
||
data_livrare: cells[5].textContent.trim(),
|
||
dimensiune: cells[6].textContent.trim(),
|
||
com_achiz_client: cells[7].textContent.trim(),
|
||
nr_linie_com_client: cells[8].textContent.trim(),
|
||
customer_name: cells[9].textContent.trim(),
|
||
customer_article_number: cells[10].textContent.trim()
|
||
};
|
||
|
||
// Check for Electron thermal printing first (best option)
|
||
if (window.ThermalPrinter) {
|
||
console.log('🔥 Using Electron thermal printer');
|
||
handleElectronThermalPrint(orderData, printerName, quantity, prodOrder);
|
||
return;
|
||
}
|
||
|
||
// Fallback to Windows service (if available)
|
||
checkPrintServiceStatus()
|
||
.then(serviceStatus => {
|
||
if (serviceStatus) {
|
||
console.log('🖨️ Using Windows print service');
|
||
return handleServicePrint(orderId, printerName, quantity, prodOrder);
|
||
} else {
|
||
throw new Error('No print service available. Please install the Quality Print Desktop app or Windows service.');
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Direct print error:', error);
|
||
|
||
let errorMessage = '❌ Direct printing failed: ' + error.message;
|
||
|
||
if (error.message.includes('service') || error.message.includes('available')) {
|
||
errorMessage += '\n\n💡 Solutions:\n' +
|
||
'1. Download and install Quality Print Desktop app (recommended)\n' +
|
||
'2. Install the Windows print service\n' +
|
||
'3. Use PDF generation as alternative';
|
||
}
|
||
|
||
alert(errorMessage);
|
||
});
|
||
}
|
||
|
||
// Handle Electron thermal printing (best option)
|
||
function handleElectronThermalPrint(orderData, printerName, quantity, prodOrder) {
|
||
const button = document.getElementById('print-label-btn');
|
||
const originalText = button.textContent;
|
||
button.textContent = 'Printing...';
|
||
button.disabled = true;
|
||
|
||
// Print each label sequentially
|
||
printSequentialLabels(orderData, printerName, quantity, 0)
|
||
.then(() => {
|
||
alert(`✅ Successfully printed ${quantity} labels!\n📊 Order: ${prodOrder}\n🖨️ Printer: ${printerName || 'Default Printer'}\n🔥 Using Electron Thermal Printing`);
|
||
|
||
// Update database status
|
||
updatePrintedStatus(orderData.id);
|
||
})
|
||
.catch(error => {
|
||
console.error('Electron thermal print error:', error);
|
||
alert(`❌ Thermal printing failed: ${error.message}\n\n💡 Try using PDF generation instead.`);
|
||
})
|
||
.finally(() => {
|
||
button.textContent = originalText;
|
||
button.disabled = false;
|
||
});
|
||
}
|
||
|
||
// Print labels sequentially for better reliability
|
||
async function printSequentialLabels(orderData, printerName, totalQuantity, currentIndex) {
|
||
if (currentIndex >= totalQuantity) {
|
||
return; // All labels printed
|
||
}
|
||
|
||
const labelNumber = currentIndex + 1;
|
||
const sequentialId = `${orderData.comanda_productie}-${labelNumber.toString().padStart(3, '0')}`;
|
||
|
||
// Generate label data for this specific label
|
||
const labelData = [
|
||
{
|
||
type: 'text',
|
||
value: 'INNOFA ROMANIA SRL',
|
||
style: { fontWeight: 'bold', fontSize: '12px', textAlign: 'center', marginBottom: '5px' }
|
||
},
|
||
{
|
||
type: 'text',
|
||
value: orderData.customer_name || 'N/A',
|
||
style: { fontSize: '11px', textAlign: 'center', marginBottom: '3px' }
|
||
},
|
||
{
|
||
type: 'text',
|
||
value: '─'.repeat(32),
|
||
style: { textAlign: 'center', fontSize: '8px', marginBottom: '2px' }
|
||
},
|
||
{
|
||
type: 'text',
|
||
value: `Quantity: ${orderData.cantitate || '0'}`,
|
||
style: { fontSize: '10px', marginBottom: '2px' }
|
||
},
|
||
{
|
||
type: 'text',
|
||
value: `Customer Order: ${orderData.com_achiz_client || 'N/A'}-${orderData.nr_linie_com_client || '00'}`,
|
||
style: { fontSize: '10px', marginBottom: '2px' }
|
||
},
|
||
{
|
||
type: 'text',
|
||
value: `Delivery: ${orderData.data_livrare || 'N/A'}`,
|
||
style: { fontSize: '10px', marginBottom: '2px' }
|
||
},
|
||
{
|
||
type: 'text',
|
||
value: `Size: ${orderData.dimensiune || 'N/A'}`,
|
||
style: { fontSize: '10px', marginBottom: '2px' }
|
||
},
|
||
{
|
||
type: 'text',
|
||
value: `Article: ${orderData.customer_article_number || 'N/A'}`,
|
||
style: { fontSize: '10px', marginBottom: '3px' }
|
||
},
|
||
{
|
||
type: 'text',
|
||
value: '─'.repeat(32),
|
||
style: { textAlign: 'center', fontSize: '8px', marginBottom: '3px' }
|
||
},
|
||
{
|
||
type: 'barCode',
|
||
value: sequentialId,
|
||
height: 40,
|
||
width: 2,
|
||
displayValue: true,
|
||
fontsize: 8,
|
||
style: { textAlign: 'center', marginBottom: '5px' }
|
||
},
|
||
{
|
||
type: 'text',
|
||
value: `Label ${labelNumber}/${totalQuantity}`,
|
||
style: { fontSize: '8px', textAlign: 'center', marginBottom: '5px' }
|
||
},
|
||
{
|
||
type: 'text',
|
||
value: ' ', // Add spacing
|
||
style: { fontSize: '6px' }
|
||
}
|
||
];
|
||
|
||
const printOptions = {
|
||
printerName: printerName || 'default',
|
||
pageSize: '80mm',
|
||
margin: '2mm 2mm 2mm 2mm',
|
||
copies: 1,
|
||
silent: true,
|
||
preview: false,
|
||
timeOutPerLine: 200
|
||
};
|
||
|
||
console.log(`🏷️ Printing label ${labelNumber}/${totalQuantity}: ${sequentialId}`);
|
||
|
||
try {
|
||
// Print this label
|
||
await window.ThermalPrinter.print(labelData, printOptions);
|
||
|
||
// Small delay between labels
|
||
await new Promise(resolve => setTimeout(resolve, 300));
|
||
|
||
// Print next label
|
||
return printSequentialLabels(orderData, printerName, totalQuantity, currentIndex + 1);
|
||
} catch (error) {
|
||
throw new Error(`Failed to print label ${labelNumber}: ${error.message}`);
|
||
}
|
||
}
|
||
|
||
// Handle Windows service printing (fallback)
|
||
function handleServicePrint(orderId, printerName, quantity, prodOrder) {
|
||
return generatePrintablePDF(orderId, true) // true for paper-saving mode
|
||
.then(pdfUrl => {
|
||
const printData = {
|
||
pdf_url: pdfUrl,
|
||
printer: printerName,
|
||
copies: 1,
|
||
order_id: parseInt(orderId),
|
||
paper_saving: true
|
||
};
|
||
|
||
return fetch('http://localhost:8899/print-pdf', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify(printData)
|
||
});
|
||
})
|
||
.then(response => {
|
||
if (!response.ok) {
|
||
throw new Error(`Print service error: ${response.status}`);
|
||
}
|
||
return response.json();
|
||
})
|
||
.then(result => {
|
||
if (result.success) {
|
||
alert(`✅ Successfully sent ${quantity} labels to printer!\n📊 Order: ${prodOrder}\n<EFBFBD>️ Printer: ${printerName || 'Default'}\n📄 Job ID: ${result.job_id || 'N/A'}`);
|
||
|
||
// Update database status and refresh table
|
||
updatePrintedStatus(parseInt(orderId));
|
||
} else {
|
||
throw new Error(result.error || 'Unknown print error');
|
||
}
|
||
});
|
||
}
|
||
|
||
// Helper function to check print service status
|
||
function checkPrintServiceStatus() {
|
||
return fetch('http://localhost:8899/status', {
|
||
method: 'GET',
|
||
mode: 'cors'
|
||
})
|
||
.then(response => {
|
||
if (response.ok) {
|
||
return response.json().then(data => {
|
||
console.log('Print service status:', data);
|
||
return data.status === 'running';
|
||
});
|
||
}
|
||
return false;
|
||
})
|
||
.catch(error => {
|
||
console.log('Print service not available:', error.message);
|
||
return false;
|
||
});
|
||
}
|
||
|
||
// Helper function to generate PDF for printing
|
||
function generatePrintablePDF(orderId, paperSaving = true) {
|
||
return fetch(`/generate_labels_pdf/${orderId}/${paperSaving ? 'true' : 'false'}`, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
}
|
||
})
|
||
.then(response => {
|
||
if (!response.ok) {
|
||
throw new Error(`Failed to generate PDF: ${response.status}`);
|
||
}
|
||
return response.blob();
|
||
})
|
||
.then(blob => {
|
||
// Create a temporary URL for the PDF
|
||
return URL.createObjectURL(blob);
|
||
});
|
||
}
|
||
|
||
function handlePDFGeneration(selectedRow) {
|
||
const orderId = selectedRow.dataset.orderId;
|
||
const quantityCell = selectedRow.querySelector('td:nth-child(5)');
|
||
const quantity = quantityCell ? parseInt(quantityCell.textContent) : 1;
|
||
const prodOrderCell = selectedRow.querySelector('td:nth-child(2)');
|
||
const prodOrder = prodOrderCell ? prodOrderCell.textContent.trim() : 'N/A';
|
||
|
||
const button = document.getElementById('print-label-btn');
|
||
const originalText = button.textContent;
|
||
button.textContent = 'Generating PDF...';
|
||
button.disabled = true;
|
||
|
||
console.log(`Generating PDF for order ${orderId} with ${quantity} labels`);
|
||
|
||
// Always use paper-saving mode (optimized for thermal label printers)
|
||
const paperSavingMode = 'true';
|
||
|
||
console.log(`Using paper-saving mode for optimal label printing`);
|
||
|
||
// Generate PDF with paper-saving mode enabled
|
||
fetch(`/generate_labels_pdf/${orderId}/${paperSavingMode}`, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
}
|
||
})
|
||
.then(response => {
|
||
if (!response.ok) {
|
||
throw new Error(`HTTP error! status: ${response.status}`);
|
||
}
|
||
return response.blob();
|
||
})
|
||
.then(blob => {
|
||
// Create blob URL for PDF
|
||
const url = window.URL.createObjectURL(blob);
|
||
|
||
// Create download link for PDF
|
||
const a = document.createElement('a');
|
||
a.href = url;
|
||
a.download = `labels_${prodOrder}_${quantity}pcs.pdf`;
|
||
document.body.appendChild(a);
|
||
a.click();
|
||
document.body.removeChild(a);
|
||
|
||
// Also open PDF in new tab for printing
|
||
const printWindow = window.open(url, '_blank');
|
||
if (printWindow) {
|
||
printWindow.focus();
|
||
|
||
// Wait for PDF to load, then show print dialog and cleanup
|
||
setTimeout(() => {
|
||
printWindow.print();
|
||
|
||
// Clean up blob URL after print dialog is shown
|
||
setTimeout(() => {
|
||
window.URL.revokeObjectURL(url);
|
||
}, 2000);
|
||
}, 1500);
|
||
} else {
|
||
// If popup was blocked, clean up immediately
|
||
setTimeout(() => {
|
||
window.URL.revokeObjectURL(url);
|
||
}, 1000);
|
||
}
|
||
|
||
// Show success message
|
||
alert(`✅ PDF generated successfully!\n📊 Order: ${prodOrder}\n📦 Labels: ${quantity} pieces\n\nThe PDF has been downloaded and opened for printing.\n\n📋 The order has been marked as printed and removed from the unprinted orders list.`);
|
||
|
||
// Refresh the orders table to reflect printed status
|
||
// This will automatically hide the printed order from the unprinted orders list
|
||
setTimeout(() => {
|
||
refreshUnprintedOrdersTable();
|
||
}, 1000);
|
||
})
|
||
.catch(error => {
|
||
console.error('Error generating PDF:', error);
|
||
alert('❌ Failed to generate PDF labels. Error: ' + error.message);
|
||
})
|
||
.finally(() => {
|
||
// Reset button state
|
||
button.textContent = originalText;
|
||
button.disabled = false;
|
||
});
|
||
}
|
||
|
||
function updatePrintedStatus(orderId) {
|
||
// Call backend to update printed status for direct printing
|
||
fetch(`/update_printed_status/${orderId}`, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
}
|
||
})
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
console.log('Updated printed status:', data);
|
||
// Refresh table
|
||
setTimeout(() => {
|
||
refreshUnprintedOrdersTable();
|
||
}, 1000);
|
||
})
|
||
.catch(error => {
|
||
console.error('Error updating printed status:', error);
|
||
});
|
||
}
|
||
|
||
// Function to refresh the unprinted orders table
|
||
function refreshUnprintedOrdersTable() {
|
||
console.log('Refreshing unprinted orders table...');
|
||
const button = document.getElementById('check-db-btn');
|
||
|
||
// Show refreshing state
|
||
const originalText = button.textContent;
|
||
button.textContent = 'Refreshing...';
|
||
button.disabled = true;
|
||
|
||
fetch('/get_unprinted_orders')
|
||
.then(response => {
|
||
if (response.status === 403) {
|
||
return response.json().then(errorData => {
|
||
throw new Error(`Access Denied: ${errorData.error}`);
|
||
});
|
||
} else if (!response.ok) {
|
||
return response.text().then(text => {
|
||
throw new Error(`HTTP ${response.status}: ${text}`);
|
||
});
|
||
}
|
||
return response.json();
|
||
})
|
||
.then(data => {
|
||
console.log('Refreshed data:', data);
|
||
const tbody = document.getElementById('unprinted-orders-table');
|
||
tbody.innerHTML = '';
|
||
|
||
if (data.length === 0) {
|
||
// No unprinted orders left
|
||
tbody.innerHTML = '<tr><td colspan="15" style="text-align: center; padding: 20px; color: #28a745;"><strong>✅ All orders have been printed!</strong><br><small>No unprinted orders remaining.</small></td></tr>';
|
||
|
||
// Clear label preview
|
||
clearLabelPreview();
|
||
} else {
|
||
// Populate table with remaining unprinted orders
|
||
data.forEach((order, index) => {
|
||
const tr = document.createElement('tr');
|
||
tr.dataset.orderId = order.id;
|
||
tr.dataset.orderIndex = index;
|
||
tr.style.cursor = 'pointer';
|
||
tr.innerHTML = `
|
||
<td style="font-size: 9px;">${order.id}</td>
|
||
<td style="font-size: 9px;"><strong>${order.comanda_productie}</strong></td>
|
||
<td style="font-size: 9px;">${order.cod_articol || '-'}</td>
|
||
<td style="font-size: 9px;">${order.descr_com_prod}</td>
|
||
<td style="text-align: right; font-weight: 600; font-size: 9px;">${order.cantitate}</td>
|
||
<td style="text-align: center; font-size: 9px;">
|
||
${order.data_livrare ? new Date(order.data_livrare).toLocaleDateString() : '-'}
|
||
</td>
|
||
<td style="text-align: center; font-size: 9px;">${order.dimensiune || '-'}</td>
|
||
<td style="font-size: 9px;">${order.com_achiz_client || '-'}</td>
|
||
<td style="text-align: right; font-size: 9px;">${order.nr_linie_com_client || '-'}</td>
|
||
<td style="font-size: 9px;">${order.customer_name || '-'}</td>
|
||
<td style="font-size: 9px;">${order.customer_article_number || '-'}</td>
|
||
<td style="font-size: 9px;">${order.open_for_order || '-'}</td>
|
||
<td style="text-align: right; font-size: 9px;">${order.line_number || '-'}</td>
|
||
<td style="text-align: center; font-size: 9px;">
|
||
${order.printed_labels == 1 ?
|
||
'<span style="color: #28a745; font-weight: bold;">✓ Yes</span>' :
|
||
'<span style="color: #dc3545;">✗ No</span>'}
|
||
</td>
|
||
<td style="font-size: 9px; color: #6c757d;">
|
||
${order.created_at ? new Date(order.created_at).toLocaleString() : '-'}
|
||
</td>
|
||
`;
|
||
|
||
// Add click event for row selection
|
||
tr.addEventListener('click', function() {
|
||
console.log('Row clicked:', order.id);
|
||
|
||
// Remove selection from other rows
|
||
document.querySelectorAll('.print-module-table tbody tr').forEach(row => {
|
||
row.classList.remove('selected');
|
||
const cells = row.querySelectorAll('td');
|
||
cells.forEach(cell => {
|
||
cell.style.backgroundColor = '';
|
||
cell.style.color = '';
|
||
});
|
||
});
|
||
|
||
// Select this row
|
||
this.classList.add('selected');
|
||
|
||
// Force visual selection with inline styles
|
||
const cells = this.querySelectorAll('td');
|
||
cells.forEach(cell => {
|
||
cell.style.backgroundColor = '#007bff';
|
||
cell.style.color = 'white';
|
||
});
|
||
|
||
// Update label preview with selected order data
|
||
updateLabelPreview(order);
|
||
});
|
||
|
||
tbody.appendChild(tr);
|
||
});
|
||
|
||
// Auto-select first row
|
||
setTimeout(() => {
|
||
const firstRow = document.querySelector('.print-module-table tbody tr');
|
||
if (firstRow && !firstRow.querySelector('td[colspan]')) { // Don't select if it's the "no data" row
|
||
firstRow.classList.add('selected');
|
||
const cells = firstRow.querySelectorAll('td');
|
||
cells.forEach(cell => {
|
||
cell.style.backgroundColor = '#007bff';
|
||
cell.style.color = 'white';
|
||
});
|
||
updateLabelPreview(data[0]);
|
||
}
|
||
}, 100);
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Error refreshing orders:', error);
|
||
const tbody = document.getElementById('unprinted-orders-table');
|
||
tbody.innerHTML = '<tr><td colspan="15" style="text-align: center; padding: 20px; color: #dc3545;"><strong>❌ Failed to refresh data</strong><br><small>' + error.message + '</small></td></tr>';
|
||
})
|
||
.finally(() => {
|
||
button.textContent = originalText;
|
||
button.disabled = false;
|
||
});
|
||
}
|
||
|
||
// Function to clear label preview when no orders are available
|
||
function clearLabelPreview() {
|
||
document.getElementById('customer-name-row').textContent = 'No orders available';
|
||
document.getElementById('quantity-ordered-value').textContent = '0';
|
||
document.getElementById('client-order-info').textContent = 'N/A';
|
||
document.getElementById('delivery-date-value').textContent = 'N/A';
|
||
document.getElementById('size-value').textContent = 'N/A';
|
||
document.getElementById('description-value').textContent = 'N/A';
|
||
document.getElementById('article-code-value').textContent = 'N/A';
|
||
document.getElementById('prod-order-value').textContent = 'N/A';
|
||
document.getElementById('barcode-text').textContent = 'N/A';
|
||
document.getElementById('vertical-barcode-text').textContent = '000000-00';
|
||
}
|
||
|
||
// Download Electron app function
|
||
function downloadElectronApp() {
|
||
// Download the Quality Print Desktop v1.0.0
|
||
const link = document.createElement('a');
|
||
link.href = '/download/desktop-app';
|
||
link.download = 'Quality_Print_Desktop_v1.0.0.zip';
|
||
document.body.appendChild(link);
|
||
link.click();
|
||
document.body.removeChild(link);
|
||
|
||
// Show success message
|
||
showNotification('📱 Quality Print Desktop v1.0.0 download started!', 'success');
|
||
}
|
||
|
||
function downloadPortableApp() {
|
||
// Download the portable Windows version
|
||
const link = document.createElement('a');
|
||
link.href = '/download/portable-app';
|
||
link.download = 'Quality_Print_Desktop_Portable_v1.0.0_Windows.zip';
|
||
document.body.appendChild(link);
|
||
link.click();
|
||
document.body.removeChild(link);
|
||
|
||
// Show success message
|
||
showNotification('💻 Portable Windows version download started!', 'success');
|
||
}
|
||
|
||
document.getElementById('print-label-btn').addEventListener('click', function() {
|
||
console.log('Generate PDF logic here');
|
||
});
|
||
</script>
|
||
{% endblock %} |