created module saved preview ptinting
This commit is contained in:
1844
backup/print_module.htmlold 3.bak
Normal file
1844
backup/print_module.htmlold 3.bak
Normal file
File diff suppressed because it is too large
Load Diff
1110
backup/print_moduleold but good.html
Normal file
1110
backup/print_moduleold but good.html
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,13 +1,11 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
{% block head %}
|
{% block head %}
|
||||||
<!-- JsBarcode library for real barcode generation -->
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/jsbarcode@3.11.5/dist/JsBarcode.all.min.js"></script>
|
|
||||||
<style>
|
<style>
|
||||||
#label-preview {
|
#label-preview {
|
||||||
background: #fafafa;
|
background: #fafafa;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Enhanced table styling */
|
/* Enhanced table styling */
|
||||||
@@ -37,168 +35,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Print Progress Modal Styles */
|
/* Print Progress Modal Styles */
|
||||||
.print-progress-modal {
|
|
||||||
display: none !important;
|
|
||||||
position: fixed;
|
|
||||||
z-index: 9999;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background-color: rgba(0, 0, 0, 0.7);
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.print-progress-modal.show {
|
|
||||||
display: flex !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.print-progress-content {
|
|
||||||
background-color: white;
|
|
||||||
padding: 30px;
|
|
||||||
border-radius: 15px;
|
|
||||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
|
|
||||||
min-width: 500px;
|
|
||||||
max-width: 600px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.print-progress-content h3 {
|
|
||||||
margin: 0 0 20px 0;
|
|
||||||
color: #333;
|
|
||||||
font-size: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress-info {
|
|
||||||
margin-bottom: 15px;
|
|
||||||
color: #666;
|
|
||||||
font-size: 16px;
|
|
||||||
min-height: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress-bar-container {
|
|
||||||
width: 100%;
|
|
||||||
height: 35px;
|
|
||||||
background-color: #e9ecef;
|
|
||||||
border-radius: 18px;
|
|
||||||
overflow: hidden;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
box-shadow: inset 0 2px 6px rgba(0, 0, 0, 0.15);
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress-bar {
|
|
||||||
height: 100%;
|
|
||||||
background: linear-gradient(90deg, #28a745 0%, #20c997 100%);
|
|
||||||
width: 0%;
|
|
||||||
transition: width 0.4s ease;
|
|
||||||
border-radius: 18px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
color: white;
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress-bar.error {
|
|
||||||
background: linear-gradient(90deg, #dc3545 0%, #c82333 100%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress-bar.paused {
|
|
||||||
background: linear-gradient(90deg, #ffc107 0%, #ff9800 100%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress-details {
|
|
||||||
font-size: 20px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #28a745;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress-details.error {
|
|
||||||
color: #dc3545;
|
|
||||||
}
|
|
||||||
|
|
||||||
.print-status-log {
|
|
||||||
max-height: 150px;
|
|
||||||
overflow-y: auto;
|
|
||||||
background: #f8f9fa;
|
|
||||||
border: 1px solid #dee2e6;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 10px;
|
|
||||||
margin: 15px 0;
|
|
||||||
text-align: left;
|
|
||||||
font-family: monospace;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.print-status-log div {
|
|
||||||
padding: 3px 0;
|
|
||||||
color: #495057;
|
|
||||||
}
|
|
||||||
|
|
||||||
.print-status-log div.success {
|
|
||||||
color: #28a745;
|
|
||||||
}
|
|
||||||
|
|
||||||
.print-status-log div.error {
|
|
||||||
color: #dc3545;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.print-status-log div.warning {
|
|
||||||
color: #ffc107;
|
|
||||||
}
|
|
||||||
|
|
||||||
.print-control-buttons {
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
justify-content: center;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.print-control-btn {
|
|
||||||
padding: 10px 20px;
|
|
||||||
border: none;
|
|
||||||
border-radius: 8px;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 600;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.print-control-btn:hover {
|
|
||||||
transform: translateY(-2px);
|
|
||||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.print-control-btn.pause {
|
|
||||||
background: #ffc107;
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.print-control-btn.resume {
|
|
||||||
background: #28a745;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.print-control-btn.reprint {
|
|
||||||
background: #17a2b8;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.print-control-btn.cancel {
|
|
||||||
background: #dc3545;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.print-control-btn:disabled {
|
|
||||||
opacity: 0.5;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -209,20 +45,17 @@
|
|||||||
<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="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>
|
<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>
|
||||||
|
|
||||||
<!-- Add link to pairing key management -->
|
<!-- Pairing Keys Section - Only show dropdown if multiple keys exist -->
|
||||||
<div style="width: 100%; text-align: center; margin-bottom: 10px;">
|
<div style="width: 100%; text-align: center; margin-bottom: 15px;">
|
||||||
<a href="{{ url_for('main.download_extension') }}" class="btn btn-info btn-sm" target="_blank">🔑 Manage Pairing Keys</a>
|
<!-- Dropdown for multiple keys (hidden by default) -->
|
||||||
|
<div id="client-select-container" style="display: none; margin-bottom: 8px;">
|
||||||
|
<label for="client-select" style="font-size: 11px; font-weight: 600; display: block; margin-bottom: 4px;">Select Printer/Client:</label>
|
||||||
|
<select id="client-select" class="form-control form-control-sm" style="width: 85%; margin: 0 auto; font-size: 11px;"></select>
|
||||||
|
</div>
|
||||||
|
<!-- Manage Keys Button -->
|
||||||
|
<a href="{{ url_for('main.download_extension') }}" class="btn btn-info btn-sm" target="_blank" style="font-size: 11px; padding: 4px 12px;">🔑 Manage Keys</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Client/Printer selection dropdown -->
|
|
||||||
<div style="width: 100%; text-align: center; margin-bottom: 10px;">
|
|
||||||
<label for="client-select" style="font-weight: 600;">Select Printer/Client:</label>
|
|
||||||
<select id="client-select" class="form-control form-control-sm" style="width: 80%; margin: 0 auto;"></select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Display pairing key for selected client -->
|
|
||||||
<div id="pairing-key-display" style="width: 100%; text-align: center; margin-bottom: 15px; font-size: 13px; color: #007bff; font-family: monospace;"></div>
|
|
||||||
|
|
||||||
<!-- Label Preview Section -->
|
<!-- Label Preview Section -->
|
||||||
<div id="label-preview" style="border: 1px solid #ddd; padding: 10px; position: relative; background: #fafafa; width: 301px; height: 434.7px;">
|
<div id="label-preview" style="border: 1px solid #ddd; padding: 10px; position: relative; background: #fafafa; width: 301px; height: 434.7px;">
|
||||||
<!-- Label content rectangle -->
|
<!-- Label content rectangle -->
|
||||||
@@ -307,10 +140,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Barcode Frame - positioned 10px below rectangle with 2mm side margins -->
|
<!-- Barcode Frame - positioned 10px below rectangle, centered, 90% of label width -->
|
||||||
<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;">
|
<div id="barcode-frame" style="position: absolute; top: 395px; left: 50%; transform: translateX(-50%); width: 90%; max-width: 270px; height: 50px; background: white; display: flex; flex-direction: column; align-items: center; justify-content: center;">
|
||||||
<!-- Code 128 Barcode representation -->
|
<!-- Code 128 Barcode representation -->
|
||||||
<svg id="barcode-display" style="width: 280px; height: 40px;"></svg>
|
<svg id="barcode-display" style="width: 100%; height: 40px;"></svg>
|
||||||
|
|
||||||
<!-- Barcode text below the bars -->
|
<!-- 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;">
|
<div id="barcode-text" style="font-size: 8px; font-family: 'Courier New', monospace; margin-top: 2px; text-align: center; font-weight: bold;">
|
||||||
@@ -318,13 +151,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Vertical Barcode Frame - positioned on the right side of the label -->
|
<!-- Vertical Barcode Frame - positioned on the right side, rotated 90 degrees, spans the height of main rectangle -->
|
||||||
<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;">
|
<div id="vertical-barcode-frame" style="position: absolute; top: 50px; left: 270px; width: 321.3px; height: 40px; background: white; display: flex; align-items: center; justify-content: center; transform: rotate(90deg); transform-origin: left center;">
|
||||||
<!-- Vertical Code 128 Barcode representation -->
|
<!-- Vertical Code 128 Barcode representation -->
|
||||||
<svg id="vertical-barcode-display" style="width: 45px; height: 280px;"></svg>
|
<svg id="vertical-barcode-display" style="width: 100%; height: 35px;"></svg>
|
||||||
|
|
||||||
<!-- Vertical barcode text -->
|
<!-- 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;">
|
<div id="vertical-barcode-text" style="position: absolute; bottom: -15px; font-size: 7px; font-family: 'Courier New', monospace; text-align: center; font-weight: bold; width: 100%;">
|
||||||
<!-- Vertical barcode text will be populated here -->
|
<!-- Vertical barcode text will be populated here -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -333,75 +166,57 @@
|
|||||||
<!-- Print Options -->
|
<!-- Print Options -->
|
||||||
<div style="width: 100%; margin-top: 20px;">
|
<div style="width: 100%; margin-top: 20px;">
|
||||||
<!-- Print Method Selection -->
|
<!-- Print Method Selection -->
|
||||||
<div style="margin-bottom: 15px;">
|
<div style="margin-bottom: 15px;" role="group" aria-labelledby="print-method-label">
|
||||||
<label style="font-size: 12px; font-weight: 600; color: #495057; margin-bottom: 8px; display: block;">
|
<div id="print-method-label" style="font-size: 12px; font-weight: 600; color: #495057; margin-bottom: 8px;">
|
||||||
📄 Print Method:
|
📄 Print Method:
|
||||||
</label>
|
</div>
|
||||||
|
|
||||||
<div class="form-check mb-2">
|
<div class="form-check" style="margin-bottom: 6px;">
|
||||||
<input class="form-check-input" type="radio" name="printMethod" id="qzTrayPrint" value="qztray">
|
<input class="form-check-input" type="radio" name="printMethod" id="qzTrayPrint" value="qztray" checked>
|
||||||
<label class="form-check-label" for="qzTrayPrint" style="font-size: 11px; line-height: 1.3;">
|
<label class="form-check-label" for="qzTrayPrint" style="font-size: 11px; line-height: 1.2;">
|
||||||
<strong>QZ Tray Direct Print</strong> <span id="qztray-status" class="badge badge-secondary">Checking...</span><br>
|
<strong>🖨️ Direct Print</strong> <span id="qztray-status" class="badge badge-success" style="font-size: 9px; padding: 2px 6px;">Ready</span>
|
||||||
<span class="text-muted">Direct print to thermal label printer</span>
|
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-check mb-2">
|
<div class="form-check" id="pdf-option-container" style="display: none; margin-bottom: 6px;">
|
||||||
<input class="form-check-input" type="radio" name="printMethod" id="pdfGenerate" value="pdf" checked>
|
<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;">
|
<label class="form-check-label" for="pdfGenerate" style="font-size: 11px; line-height: 1.2;">
|
||||||
<strong>Generate PDF</strong><br>
|
<strong>📄 PDF Export</strong> <span class="text-muted" style="font-size: 10px;">(fallback)</span>
|
||||||
<span class="text-muted">Create PDF for manual printing (recommended)</span>
|
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Printer Selection for QZ Tray -->
|
<!-- Printer Selection for QZ Tray (Compact) -->
|
||||||
<div id="qztray-printer-selection" style="margin-bottom: 15px; display: none;">
|
<div id="qztray-printer-selection" style="margin-bottom: 10px;">
|
||||||
<label style="font-size: 12px; font-weight: 600; color: #495057; margin-bottom: 5px; display: block;">
|
<label for="qztray-printer-select" style="font-size: 11px; font-weight: 600; color: #495057; margin-bottom: 3px; display: block;">
|
||||||
🖨️ Select Printer:
|
Printer:
|
||||||
</label>
|
</label>
|
||||||
<select id="qztray-printer-select" class="form-control form-control-sm" style="font-size: 11px; padding: 4px 8px;">
|
<select id="qztray-printer-select" class="form-control form-control-sm" style="font-size: 11px; padding: 3px 6px;">
|
||||||
<option value="">Loading printers...</option>
|
<option value="">Loading...</option>
|
||||||
</select>
|
</select>
|
||||||
<small class="text-muted" style="font-size: 10px;">Choose your thermal label printer</small>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Print Button -->
|
<!-- Print Button -->
|
||||||
<div style="width: 100%; text-align: center; margin-bottom: 15px;">
|
<div style="width: 100%; text-align: center; margin-bottom: 10px;">
|
||||||
<button id="print-label-btn" class="btn btn-success" style="font-size: 14px; padding: 10px 30px; border-radius: 6px; font-weight: 600;">
|
<button id="print-label-btn" class="btn btn-success" style="font-size: 13px; padding: 8px 24px; border-radius: 5px; font-weight: 600;">
|
||||||
📄 Generate PDF Labels
|
<EFBFBD>️ Print Labels
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Print Information -->
|
<!-- Print Information -->
|
||||||
<div style="width: 100%; text-align: center; color: #6c757d; font-size: 11px; line-height: 1.4;">
|
<div style="width: 100%; text-align: center; color: #6c757d; font-size: 10px; line-height: 1.3;">
|
||||||
<div style="margin-bottom: 5px;">Creates sequential labels based on quantity</div>
|
<small>(e.g., CP00000711-001, 002, ...)</small>
|
||||||
<small>(e.g., CP00000711-001 to CP00000711-063)</small>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- QZ Tray Installation Info -->
|
<!-- QZ Tray Installation Info - Simplified -->
|
||||||
<div id="qztray-info" style="width: 100%; margin-top: 15px; padding-top: 15px; border-top: 1px solid #e9ecef; display: none;">
|
<div id="qztray-info" style="width: 100%; margin-top: 15px; padding-top: 15px; border-top: 1px solid #e9ecef;">
|
||||||
<div style="background: #e8f4fd; border: 1px solid #bee5eb; border-radius: 6px; padding: 12px; margin-bottom: 10px;">
|
<div style="background: #f8f9fa; border: 1px solid #dee2e6; border-radius: 6px; padding: 10px; text-align: center;">
|
||||||
<div style="font-size: 11px; color: #0c5460; margin-bottom: 8px;">
|
<div style="font-size: 10px; color: #495057; margin-bottom: 8px;">
|
||||||
<strong>🖨️ QZ Tray Direct Printing</strong>
|
QZ Tray is required for direct printing
|
||||||
</div>
|
</div>
|
||||||
<div style="font-size: 10px; color: #495057; margin-bottom: 10px; line-height: 1.3;">
|
<a href="https://filebrowser.moto-adv.com/filebrowser/api/public/dl/Fk0ZaiEY/QP_Tray/qz-tray-2.2.6-SNAPSHOT-x86_64.exe?token=TJ7gSu3CRcWWQuyFLoZv5I8j4diDjP47DDqWRtM0oKAx-2_orj1stfKPJsuuqKR9mE2GQNm1jlZ0BPR7lfZ3gHmu56SkY9fC5AJlC9n_80oX643ojlGc-U7XVb1SDd0w" class="btn btn-outline-secondary btn-sm" style="font-size: 10px; padding: 4px 16px;">
|
||||||
Professional printing solution • ZPL thermal labels • Direct hardware access
|
📥 Download QZ Tray
|
||||||
</div>
|
|
||||||
<div style="margin-bottom: 10px;">
|
|
||||||
<button onclick="initializeQZTray()" class="btn btn-primary btn-sm" style="font-size: 10px; padding: 4px 12px;">
|
|
||||||
🔄 Reconnect to QZ Tray
|
|
||||||
</button>
|
|
||||||
<button onclick="testQZConnection()" class="btn btn-info btn-sm" style="font-size: 10px; padding: 4px 12px;">
|
|
||||||
🔍 Test Connection
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<a href="https://qz.io/download/" target="_blank" class="btn btn-secondary btn-sm" style="font-size: 10px; padding: 4px 12px; text-decoration: none;">
|
|
||||||
📥 Download QZ Tray (Free)
|
|
||||||
</a>
|
</a>
|
||||||
<div style="font-size: 9px; color: #6c757d; margin-top: 8px; line-height: 1.2;">
|
|
||||||
<strong>Setup:</strong> 1. Install QZ Tray → 2. Start the service → 3. Click "Reconnect"
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -440,49 +255,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Printing Progress Modal -->
|
<!-- JavaScript Libraries -->
|
||||||
<div id="print-progress-modal" class="print-progress-modal">
|
<!-- JsBarcode library for real barcode generation -->
|
||||||
<div class="print-progress-content">
|
<script src="https://cdn.jsdelivr.net/npm/jsbarcode@3.11.5/dist/JsBarcode.all.min.js"></script>
|
||||||
<h3>🖨️ Print Controller</h3>
|
<!-- Add html2canvas library for capturing preview as image -->
|
||||||
<div class="progress-info">
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
|
||||||
<span id="progress-text">Preparing to print...</span>
|
<!-- PATCHED QZ Tray library - works with custom server using pairing key authentication -->
|
||||||
</div>
|
<script src="{{ url_for('static', filename='qz-tray.js') }}"></script>
|
||||||
<div class="progress-bar-container">
|
<!-- Original CDN version (disabled): <script src="https://cdn.jsdelivr.net/npm/qz-tray@2.2.4/qz-tray.js"></script> -->
|
||||||
<div id="progress-bar" class="progress-bar">0%</div>
|
|
||||||
</div>
|
|
||||||
<div class="progress-details">
|
|
||||||
<span id="progress-count">0 / 0</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Print Status Log -->
|
|
||||||
<div class="print-status-log" id="print-status-log">
|
|
||||||
<div>Waiting to start...</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Control Buttons -->
|
|
||||||
<div class="print-control-buttons">
|
|
||||||
<button id="pause-print-btn" class="print-control-btn pause" style="display: none;">
|
|
||||||
⏸️ Pause
|
|
||||||
</button>
|
|
||||||
<button id="resume-print-btn" class="print-control-btn resume" style="display: none;">
|
|
||||||
▶️ Resume
|
|
||||||
</button>
|
|
||||||
<button id="reprint-last-btn" class="print-control-btn reprint" style="display: none;">
|
|
||||||
🔄 Reprint Last
|
|
||||||
</button>
|
|
||||||
<button id="cancel-print-btn" class="print-control-btn cancel" style="display: none;">
|
|
||||||
❌ Cancel
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- QZ Tray JavaScript Library -->
|
|
||||||
<!-- Add html2canvas library for capturing preview as image -->
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
|
|
||||||
<!-- PATCHED QZ Tray library - works with custom server using pairing key authentication -->
|
|
||||||
<script src="{{ url_for('static', filename='qz-tray.js') }}"></script>
|
|
||||||
<!-- Original CDN version (disabled): <script src="https://cdn.jsdelivr.net/npm/qz-tray@2.2.4/qz-tray.js"></script> -->
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// Simplified notification system
|
// Simplified notification system
|
||||||
@@ -524,6 +304,10 @@ function showNotification(message, type = 'info') {
|
|||||||
}, timeout);
|
}, timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wait for DOM to be ready before attaching event listeners
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
console.log('🚀 DOM Content Loaded - Initializing page...');
|
||||||
|
|
||||||
// Database loading functionality
|
// Database loading functionality
|
||||||
document.getElementById('check-db-btn').addEventListener('click', function() {
|
document.getElementById('check-db-btn').addEventListener('click', function() {
|
||||||
const button = this;
|
const button = this;
|
||||||
@@ -636,27 +420,7 @@ document.getElementById('check-db-btn').addEventListener('click', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Fetch pairing keys and populate dropdown
|
// Functions that don't need DOM elements (can be outside DOMContentLoaded)
|
||||||
fetch('/get_pairing_keys')
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(keys => {
|
|
||||||
const select = document.getElementById('client-select');
|
|
||||||
select.innerHTML = '';
|
|
||||||
keys.forEach(key => {
|
|
||||||
const option = document.createElement('option');
|
|
||||||
option.value = key.pairing_key;
|
|
||||||
option.textContent = key.printer_name;
|
|
||||||
select.appendChild(option);
|
|
||||||
});
|
|
||||||
// Show first key by default
|
|
||||||
if (keys.length > 0) {
|
|
||||||
document.getElementById('pairing-key-display').textContent = 'Pairing Key: ' + keys[0].pairing_key;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// Update pairing key display on selection
|
|
||||||
document.getElementById('client-select').addEventListener('change', function() {
|
|
||||||
document.getElementById('pairing-key-display').textContent = 'Pairing Key: ' + this.value;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Update label preview with order data
|
// Update label preview with order data
|
||||||
function updateLabelPreview(order) {
|
function updateLabelPreview(order) {
|
||||||
@@ -700,9 +464,58 @@ function updateLabelPreview(order) {
|
|||||||
const horizontalBarcodeData = comandaProductie ? `${comandaProductie}/001` : 'N/A';
|
const horizontalBarcodeData = comandaProductie ? `${comandaProductie}/001` : 'N/A';
|
||||||
document.getElementById('barcode-text').textContent = horizontalBarcodeData;
|
document.getElementById('barcode-text').textContent = horizontalBarcodeData;
|
||||||
|
|
||||||
|
// Generate horizontal barcode visual using JsBarcode
|
||||||
|
console.log('🔍 Attempting to generate horizontal barcode:', horizontalBarcodeData);
|
||||||
|
console.log('🔍 JsBarcode available?', typeof JsBarcode !== 'undefined');
|
||||||
|
|
||||||
|
if (horizontalBarcodeData !== 'N/A' && typeof JsBarcode !== 'undefined') {
|
||||||
|
try {
|
||||||
|
const barcodeElement = document.querySelector("#barcode-display");
|
||||||
|
console.log('🔍 Horizontal barcode element:', barcodeElement);
|
||||||
|
|
||||||
|
JsBarcode("#barcode-display", horizontalBarcodeData, {
|
||||||
|
format: "CODE128",
|
||||||
|
width: 2,
|
||||||
|
height: 40,
|
||||||
|
displayValue: false,
|
||||||
|
margin: 2
|
||||||
|
});
|
||||||
|
console.log('✅ Horizontal barcode generated successfully');
|
||||||
|
} catch (e) {
|
||||||
|
console.error('❌ Failed to generate horizontal barcode:', e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.warn('⚠️ Skipping horizontal barcode generation:',
|
||||||
|
horizontalBarcodeData === 'N/A' ? 'No data' : 'JsBarcode not loaded');
|
||||||
|
}
|
||||||
|
|
||||||
// Update vertical barcode with client order format (e.g., Abcderd/65)
|
// Update vertical barcode with client order format (e.g., Abcderd/65)
|
||||||
const verticalBarcodeData = comAchizClient && nrLinie ? `${comAchizClient}/${nrLinie}` : '000000/00';
|
const verticalBarcodeData = comAchizClient && nrLinie ? `${comAchizClient}/${nrLinie}` : '000000/00';
|
||||||
document.getElementById('vertical-barcode-text').textContent = verticalBarcodeData;
|
document.getElementById('vertical-barcode-text').textContent = verticalBarcodeData;
|
||||||
|
|
||||||
|
// Generate vertical barcode visual using JsBarcode (will be rotated by CSS)
|
||||||
|
console.log('🔍 Attempting to generate vertical barcode:', verticalBarcodeData);
|
||||||
|
|
||||||
|
if (verticalBarcodeData !== '000000/00' && typeof JsBarcode !== 'undefined') {
|
||||||
|
try {
|
||||||
|
const verticalElement = document.querySelector("#vertical-barcode-display");
|
||||||
|
console.log('🔍 Vertical barcode element:', verticalElement);
|
||||||
|
|
||||||
|
JsBarcode("#vertical-barcode-display", verticalBarcodeData, {
|
||||||
|
format: "CODE128",
|
||||||
|
width: 1.5,
|
||||||
|
height: 35,
|
||||||
|
displayValue: false,
|
||||||
|
margin: 2
|
||||||
|
});
|
||||||
|
console.log('✅ Vertical barcode generated successfully');
|
||||||
|
} catch (e) {
|
||||||
|
console.error('❌ Failed to generate vertical barcode:', e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.warn('⚠️ Skipping vertical barcode generation:',
|
||||||
|
verticalBarcodeData === '000000/00' ? 'Default value' : 'JsBarcode not loaded');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear label preview when no orders are available
|
// Clear label preview when no orders are available
|
||||||
@@ -717,6 +530,12 @@ function clearLabelPreview() {
|
|||||||
document.getElementById('prod-order-value').textContent = 'N/A';
|
document.getElementById('prod-order-value').textContent = 'N/A';
|
||||||
document.getElementById('barcode-text').textContent = 'N/A';
|
document.getElementById('barcode-text').textContent = 'N/A';
|
||||||
document.getElementById('vertical-barcode-text').textContent = '000000/00';
|
document.getElementById('vertical-barcode-text').textContent = '000000/00';
|
||||||
|
|
||||||
|
// Clear barcode SVGs
|
||||||
|
const horizontalBarcode = document.getElementById('barcode-display');
|
||||||
|
const verticalBarcode = document.getElementById('vertical-barcode-display');
|
||||||
|
if (horizontalBarcode) horizontalBarcode.innerHTML = '';
|
||||||
|
if (verticalBarcode) verticalBarcode.innerHTML = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
// QZ Tray Integration
|
// QZ Tray Integration
|
||||||
@@ -772,9 +591,13 @@ async function initializeQZTray() {
|
|||||||
console.log('📋 QZ Tray Version:', version);
|
console.log('📋 QZ Tray Version:', version);
|
||||||
|
|
||||||
// Update status
|
// Update status
|
||||||
document.getElementById('qztray-status').textContent = 'Connected';
|
document.getElementById('qztray-status').textContent = 'Ready';
|
||||||
document.getElementById('qztray-status').className = 'badge badge-success';
|
document.getElementById('qztray-status').className = 'badge badge-success';
|
||||||
|
|
||||||
|
// Show printer selection, hide PDF option
|
||||||
|
document.getElementById('qztray-printer-selection').style.display = 'block';
|
||||||
|
document.getElementById('pdf-option-container').style.display = 'none';
|
||||||
|
|
||||||
// Load available printers
|
// Load available printers
|
||||||
await loadQZTrayPrinters();
|
await loadQZTrayPrinters();
|
||||||
|
|
||||||
@@ -842,6 +665,14 @@ async function initializeQZTray() {
|
|||||||
document.getElementById('qztray-status').textContent = statusText;
|
document.getElementById('qztray-status').textContent = statusText;
|
||||||
document.getElementById('qztray-status').className = 'badge badge-danger';
|
document.getElementById('qztray-status').className = 'badge badge-danger';
|
||||||
|
|
||||||
|
// QZ Tray not available - show PDF option as fallback
|
||||||
|
document.getElementById('qztray-printer-selection').style.display = 'none';
|
||||||
|
document.getElementById('pdf-option-container').style.display = 'block';
|
||||||
|
|
||||||
|
// Switch to PDF method since QZ Tray is unavailable
|
||||||
|
document.getElementById('pdfGenerate').checked = true;
|
||||||
|
document.getElementById('qzTrayPrint').disabled = true;
|
||||||
|
|
||||||
showNotification(errorMsg, 'error');
|
showNotification(errorMsg, 'error');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1398,69 +1229,7 @@ function generateHTMLLabel(orderData, pieceNumber, totalPieces) {
|
|||||||
|
|
||||||
|
|
||||||
// Handle QZ Tray printing with enhanced controller
|
// Handle QZ Tray printing with enhanced controller
|
||||||
let printController = {
|
|
||||||
isPaused: false,
|
|
||||||
isCancelled: false,
|
|
||||||
currentLabel: 0,
|
|
||||||
totalLabels: 0,
|
|
||||||
lastPrintedLabel: 0,
|
|
||||||
failedLabels: [],
|
|
||||||
orderData: null,
|
|
||||||
printerName: null
|
|
||||||
};
|
|
||||||
|
|
||||||
function addLogEntry(message, type = 'info') {
|
|
||||||
const log = document.getElementById('print-status-log');
|
|
||||||
const entry = document.createElement('div');
|
|
||||||
entry.className = type;
|
|
||||||
const timestamp = new Date().toLocaleTimeString();
|
|
||||||
entry.textContent = `[${timestamp}] ${message}`;
|
|
||||||
log.appendChild(entry);
|
|
||||||
log.scrollTop = log.scrollHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateProgressBar(current, total, status = '') {
|
|
||||||
const progressBar = document.getElementById('progress-bar');
|
|
||||||
const progressCount = document.getElementById('progress-count');
|
|
||||||
const progressText = document.getElementById('progress-text');
|
|
||||||
const percentage = Math.round((current / total) * 100);
|
|
||||||
|
|
||||||
progressBar.style.width = `${percentage}%`;
|
|
||||||
progressBar.textContent = `${percentage}%`;
|
|
||||||
progressCount.textContent = `${current} / ${total}`;
|
|
||||||
|
|
||||||
if (status) {
|
|
||||||
progressText.textContent = status;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleQZTrayPrint(selectedRow) {
|
async function handleQZTrayPrint(selectedRow) {
|
||||||
const modal = document.getElementById('print-progress-modal');
|
|
||||||
const progressBar = document.getElementById('progress-bar');
|
|
||||||
const progressText = document.getElementById('progress-text');
|
|
||||||
const log = document.getElementById('print-status-log');
|
|
||||||
|
|
||||||
const pauseBtn = document.getElementById('pause-print-btn');
|
|
||||||
const resumeBtn = document.getElementById('resume-print-btn');
|
|
||||||
const reprintBtn = document.getElementById('reprint-last-btn');
|
|
||||||
const cancelBtn = document.getElementById('cancel-print-btn');
|
|
||||||
|
|
||||||
// Debug: Check if modal exists
|
|
||||||
console.log('🔍 Modal element:', modal);
|
|
||||||
console.log('🔍 Modal classList before:', modal ? modal.classList.toString() : 'MODAL NOT FOUND');
|
|
||||||
|
|
||||||
// Reset controller state
|
|
||||||
printController = {
|
|
||||||
isPaused: false,
|
|
||||||
isCancelled: false,
|
|
||||||
currentLabel: 0,
|
|
||||||
totalLabels: 0,
|
|
||||||
lastPrintedLabel: 0,
|
|
||||||
failedLabels: [],
|
|
||||||
orderData: null,
|
|
||||||
printerName: null
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!qzTray) {
|
if (!qzTray) {
|
||||||
await initializeQZTray();
|
await initializeQZTray();
|
||||||
@@ -1491,199 +1260,58 @@ async function handleQZTrayPrint(selectedRow) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const quantity = orderData.cantitate;
|
const quantity = orderData.cantitate;
|
||||||
printController.orderData = orderData;
|
|
||||||
printController.printerName = selectedPrinter;
|
|
||||||
printController.totalLabels = quantity;
|
|
||||||
|
|
||||||
console.log(`🖨️ Printing ${quantity} labels via QZ Tray to ${selectedPrinter}`);
|
console.log(`🖨️ Printing ${quantity} labels via QZ Tray to ${selectedPrinter}`);
|
||||||
|
showNotification(`🖨️ Printing ${quantity} labels...`, 'info');
|
||||||
|
|
||||||
// Show modal with show class for proper display
|
// Print each label sequentially
|
||||||
console.log('👉 Adding show class to modal...');
|
for (let i = 1; i <= quantity; i++) {
|
||||||
modal.classList.add('show');
|
console.log(`Printing label ${i} of ${quantity}...`);
|
||||||
// Force display with inline style to override any CSS conflicts
|
|
||||||
modal.style.display = 'flex';
|
try {
|
||||||
console.log('🔍 Modal classList after:', modal.classList.toString());
|
// Generate PDF and send to printer
|
||||||
console.log('🔍 Modal computed display:', window.getComputedStyle(modal).display);
|
await generatePDFAndPrint(selectedPrinter, orderData, i, quantity);
|
||||||
console.log('🔍 Modal inline display:', modal.style.display);
|
console.log(`✓ Label ${i} printed successfully`);
|
||||||
|
|
||||||
log.innerHTML = '';
|
|
||||||
addLogEntry(`Starting print job: ${quantity} labels`, 'info');
|
|
||||||
addLogEntry(`Printer: ${selectedPrinter}`, 'info');
|
|
||||||
addLogEntry(`Order: ${orderData.comanda_productie}`, 'info');
|
|
||||||
|
|
||||||
updateProgressBar(0, quantity, 'Preparing to print...');
|
|
||||||
progressBar.classList.remove('error', 'paused');
|
|
||||||
|
|
||||||
// Show control buttons
|
|
||||||
pauseBtn.style.display = 'inline-block';
|
|
||||||
cancelBtn.style.display = 'inline-block';
|
|
||||||
reprintBtn.style.display = 'none';
|
|
||||||
resumeBtn.style.display = 'none';
|
|
||||||
|
|
||||||
// Setup button handlers
|
|
||||||
pauseBtn.onclick = () => {
|
|
||||||
printController.isPaused = true;
|
|
||||||
pauseBtn.style.display = 'none';
|
|
||||||
resumeBtn.style.display = 'inline-block';
|
|
||||||
progressBar.classList.add('paused');
|
|
||||||
addLogEntry('Print job paused by user', 'warning');
|
|
||||||
updateProgressBar(printController.currentLabel, quantity, '⏸️ Paused - Check printer paper');
|
|
||||||
};
|
|
||||||
|
|
||||||
resumeBtn.onclick = () => {
|
|
||||||
printController.isPaused = false;
|
|
||||||
resumeBtn.style.display = 'none';
|
|
||||||
pauseBtn.style.display = 'inline-block';
|
|
||||||
progressBar.classList.remove('paused');
|
|
||||||
addLogEntry('Print job resumed', 'success');
|
|
||||||
};
|
|
||||||
|
|
||||||
reprintBtn.onclick = async () => {
|
|
||||||
if (printController.lastPrintedLabel > 0) {
|
|
||||||
addLogEntry(`Reprinting label ${printController.lastPrintedLabel}...`, 'info');
|
|
||||||
try {
|
|
||||||
await generatePDFAndPrint(selectedPrinter, orderData, printController.lastPrintedLabel, quantity);
|
|
||||||
addLogEntry(`Label ${printController.lastPrintedLabel} reprinted successfully`, 'success');
|
|
||||||
} catch (error) {
|
|
||||||
addLogEntry(`Reprint failed: ${error.message}`, 'error');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
cancelBtn.onclick = () => {
|
|
||||||
printController.isCancelled = true;
|
|
||||||
addLogEntry('Print job cancelled by user', 'error');
|
|
||||||
progressBar.classList.add('error');
|
|
||||||
updateProgressBar(printController.currentLabel, quantity, '❌ Cancelled');
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Print each label sequentially
|
|
||||||
for (let i = 1; i <= quantity; i++) {
|
|
||||||
if (printController.isCancelled) {
|
|
||||||
addLogEntry('Print job terminated', 'error');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait while paused
|
} catch (printError) {
|
||||||
while (printController.isPaused && !printController.isCancelled) {
|
console.error(`✗ Label ${i} failed:`, printError);
|
||||||
await new Promise(resolve => setTimeout(resolve, 500));
|
throw new Error(`Label ${i} failed: ${printError.message}`);
|
||||||
}
|
|
||||||
|
|
||||||
if (printController.isCancelled) break;
|
|
||||||
|
|
||||||
printController.currentLabel = i;
|
|
||||||
updateProgressBar(i - 1, quantity, `Printing label ${i} of ${quantity}...`);
|
|
||||||
addLogEntry(`Sending label ${i} to printer...`, 'info');
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Generate PDF and send to printer
|
|
||||||
await generatePDFAndPrint(selectedPrinter, orderData, i, quantity);
|
|
||||||
|
|
||||||
printController.lastPrintedLabel = i;
|
|
||||||
updateProgressBar(i, quantity, `Label ${i} printed successfully`);
|
|
||||||
addLogEntry(`✓ Label ${i} printed successfully`, 'success');
|
|
||||||
|
|
||||||
// Show reprint button after each successful print
|
|
||||||
reprintBtn.style.display = 'inline-block';
|
|
||||||
|
|
||||||
} catch (printError) {
|
|
||||||
// Print error detected
|
|
||||||
progressBar.classList.add('error');
|
|
||||||
printController.failedLabels.push(i);
|
|
||||||
addLogEntry(`✗ Label ${i} failed: ${printError.message}`, 'error');
|
|
||||||
|
|
||||||
// Pause automatically on error
|
|
||||||
printController.isPaused = true;
|
|
||||||
pauseBtn.style.display = 'none';
|
|
||||||
resumeBtn.style.display = 'inline-block';
|
|
||||||
progressBar.classList.add('paused');
|
|
||||||
updateProgressBar(i - 1, quantity, '⚠️ ERROR - Check printer (paper jam/out of paper)');
|
|
||||||
|
|
||||||
// Wait for user to resume or cancel
|
|
||||||
while (printController.isPaused && !printController.isCancelled) {
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 500));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!printController.isCancelled) {
|
|
||||||
// Retry failed label
|
|
||||||
addLogEntry(`Retrying label ${i}...`, 'warning');
|
|
||||||
await generatePDFAndPrint(selectedPrinter, orderData, i, quantity);
|
|
||||||
addLogEntry(`✓ Label ${i} printed successfully (retry)`, 'success');
|
|
||||||
printController.lastPrintedLabel = i;
|
|
||||||
progressBar.classList.remove('error');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Small delay between labels for printer processing
|
|
||||||
if (i < quantity && !printController.isCancelled) {
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 500));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!printController.isCancelled) {
|
// Small delay between labels
|
||||||
// All labels printed successfully
|
if (i < quantity) {
|
||||||
progressBar.classList.remove('paused', 'error');
|
await new Promise(resolve => setTimeout(resolve, 500));
|
||||||
updateProgressBar(quantity, quantity, '✅ All labels printed! Updating database...');
|
|
||||||
addLogEntry('All labels completed successfully', 'success');
|
|
||||||
|
|
||||||
// Hide control buttons
|
|
||||||
pauseBtn.style.display = 'none';
|
|
||||||
resumeBtn.style.display = 'none';
|
|
||||||
cancelBtn.style.display = 'none';
|
|
||||||
|
|
||||||
// Update database to mark order as printed
|
|
||||||
try {
|
|
||||||
const updateResponse = await fetch(`/update_printed_status/${orderData.id}`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!updateResponse.ok) {
|
|
||||||
console.error('Failed to update printed status in database');
|
|
||||||
addLogEntry('Database update failed', 'warning');
|
|
||||||
updateProgressBar(quantity, quantity, '⚠️ Labels printed but database update failed');
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
||||||
} else {
|
|
||||||
addLogEntry('Database updated successfully', 'success');
|
|
||||||
updateProgressBar(quantity, quantity, '✅ Complete! Refreshing table...');
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
||||||
}
|
|
||||||
} catch (dbError) {
|
|
||||||
console.error('Database update error:', dbError);
|
|
||||||
addLogEntry(`Database error: ${dbError.message}`, 'error');
|
|
||||||
updateProgressBar(quantity, quantity, '⚠️ Labels printed but database update failed');
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close modal
|
|
||||||
modal.classList.remove('show');
|
|
||||||
modal.style.display = 'none';
|
|
||||||
|
|
||||||
// Show success notification
|
|
||||||
showNotification(`✅ Successfully printed ${quantity} labels!`, 'success');
|
|
||||||
|
|
||||||
// Refresh table to show updated status
|
|
||||||
document.getElementById('check-db-btn').click();
|
|
||||||
} else {
|
|
||||||
// Job was cancelled
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
||||||
modal.classList.remove('show');
|
|
||||||
modal.style.display = 'none';
|
|
||||||
showNotification(`⚠️ Print job cancelled. ${printController.lastPrintedLabel} of ${quantity} labels printed.`, 'warning');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (printError) {
|
|
||||||
modal.classList.remove('show');
|
|
||||||
modal.style.display = 'none';
|
|
||||||
throw new Error(`Print failed: ${printError.message}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// All labels printed successfully
|
||||||
|
console.log('All labels completed successfully');
|
||||||
|
showNotification(`✅ Successfully printed ${quantity} labels!`, 'success');
|
||||||
|
|
||||||
|
// Update database to mark order as printed
|
||||||
|
try {
|
||||||
|
const updateResponse = await fetch(`/update_printed_status/${orderData.id}`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!updateResponse.ok) {
|
||||||
|
console.error('Failed to update printed status in database');
|
||||||
|
showNotification('⚠️ Labels printed but database update failed', 'warning');
|
||||||
|
} else {
|
||||||
|
console.log('Database updated successfully');
|
||||||
|
}
|
||||||
|
} catch (dbError) {
|
||||||
|
console.error('Database update error:', dbError);
|
||||||
|
showNotification('⚠️ Labels printed but database update failed', 'warning');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh table to show updated status
|
||||||
|
document.getElementById('check-db-btn').click();
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
modal.classList.remove('show');
|
|
||||||
modal.style.display = 'none';
|
|
||||||
console.error('QZ Tray print error:', error);
|
console.error('QZ Tray print error:', error);
|
||||||
showNotification('❌ QZ Tray print error: ' + error.message, 'error');
|
showNotification('❌ QZ Tray print error: ' + error.message, 'error');
|
||||||
}
|
}
|
||||||
@@ -1794,24 +1422,63 @@ function handlePDFGeneration(selectedRow) {
|
|||||||
function updatePrintMethodUI() {
|
function updatePrintMethodUI() {
|
||||||
const printMethod = document.querySelector('input[name="printMethod"]:checked').value;
|
const printMethod = document.querySelector('input[name="printMethod"]:checked').value;
|
||||||
const printerSelection = document.getElementById('qztray-printer-selection');
|
const printerSelection = document.getElementById('qztray-printer-selection');
|
||||||
const qztrayInfo = document.getElementById('qztray-info');
|
|
||||||
const printButton = document.getElementById('print-label-btn');
|
const printButton = document.getElementById('print-label-btn');
|
||||||
|
|
||||||
if (printMethod === 'qztray') {
|
if (printMethod === 'qztray') {
|
||||||
printerSelection.style.display = 'block';
|
// QZ Tray is selected - printer selection should be visible if QZ Tray is connected
|
||||||
qztrayInfo.style.display = 'block';
|
// (visibility is controlled by initializeQZTray based on connection status)
|
||||||
printButton.textContent = '🖨️ Print Labels Directly';
|
printButton.textContent = '🖨️ Print Labels';
|
||||||
printButton.className = 'btn btn-primary';
|
printButton.className = 'btn btn-primary';
|
||||||
} else {
|
} else {
|
||||||
|
// PDF is selected
|
||||||
printerSelection.style.display = 'none';
|
printerSelection.style.display = 'none';
|
||||||
qztrayInfo.style.display = 'none';
|
printButton.textContent = '📄 Generate PDF';
|
||||||
printButton.textContent = '📄 Generate PDF Labels';
|
|
||||||
printButton.className = 'btn btn-success';
|
printButton.className = 'btn btn-success';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add event listeners for print method radio buttons
|
// Close the DOMContentLoaded block that was started earlier
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
// Fetch pairing keys and populate dropdown (only show if multiple keys)
|
||||||
|
fetch('/get_pairing_keys')
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(keys => {
|
||||||
|
const select = document.getElementById('client-select');
|
||||||
|
const selectContainer = document.getElementById('client-select-container');
|
||||||
|
|
||||||
|
if (keys.length === 0) {
|
||||||
|
console.warn('⚠️ No pairing keys found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keys.length === 1) {
|
||||||
|
// Only one key - use it automatically, don't show dropdown
|
||||||
|
window.selectedPairingKey = keys[0].pairing_key;
|
||||||
|
console.log(`✅ Using single pairing key for: ${keys[0].printer_name}`);
|
||||||
|
selectContainer.style.display = 'none';
|
||||||
|
} else {
|
||||||
|
// Multiple keys - show dropdown for selection
|
||||||
|
select.innerHTML = '';
|
||||||
|
keys.forEach(key => {
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.value = key.pairing_key;
|
||||||
|
option.textContent = key.printer_name;
|
||||||
|
select.appendChild(option);
|
||||||
|
});
|
||||||
|
window.selectedPairingKey = keys[0].pairing_key;
|
||||||
|
selectContainer.style.display = 'block';
|
||||||
|
console.log(`📋 ${keys.length} pairing keys available - dropdown shown`);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('❌ Failed to fetch pairing keys:', error);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update pairing key on selection
|
||||||
|
document.getElementById('client-select').addEventListener('change', function() {
|
||||||
|
window.selectedPairingKey = this.value;
|
||||||
|
console.log(`🔑 Pairing key changed: ${this.options[this.selectedIndex].text}`);
|
||||||
|
});
|
||||||
|
|
||||||
// Add change listeners to radio buttons
|
// Add change listeners to radio buttons
|
||||||
document.querySelectorAll('input[name="printMethod"]').forEach(radio => {
|
document.querySelectorAll('input[name="printMethod"]').forEach(radio => {
|
||||||
radio.addEventListener('change', updatePrintMethodUI);
|
radio.addEventListener('change', updatePrintMethodUI);
|
||||||
@@ -1827,6 +1494,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
document.getElementById('check-db-btn').click();
|
document.getElementById('check-db-btn').click();
|
||||||
}, 500);
|
}, 500);
|
||||||
});
|
}); // End DOMContentLoaded
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user