Improve FG scan page UX and integrate scan-to-boxes with QZ Tray printing
- Redesigned scan card layout: 500px width with horizontal label-input layout - Reduced spacing and optimized for no scrolling (700px max-height) - Added auto-advance between fields when valid data is entered - Integrated scan-to-boxes popup with warehouse box assignment - Added QZ Tray printing to Quick Box Creation button - Box labels now print automatically after creation in scan workflow - Fixed warehouse blueprint URL prefix registration (/warehouse) - Updated manage_boxes to return JSON for AJAX requests - Improved error message grid layout to maintain form alignment
This commit is contained in:
@@ -1,30 +1,48 @@
|
|||||||
/* Scan Module Specific Styles */
|
/* Scan Module Specific Styles */
|
||||||
|
|
||||||
.scan-form-card {
|
.scan-form-card {
|
||||||
width: 380px;
|
width: 500px;
|
||||||
max-width: 380px;
|
max-width: 500px;
|
||||||
margin: 0 auto 20px auto;
|
margin: 0 auto 20px auto;
|
||||||
max-height: 660px;
|
max-height: 700px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
padding: 18px 18px 12px 18px;
|
padding: 12px 18px 12px 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scan-form-card h3 {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scan-form-card form {
|
.scan-form-card form {
|
||||||
display: flex;
|
display: grid;
|
||||||
flex-direction: column;
|
grid-template-columns: 140px 1fr;
|
||||||
gap: 4px;
|
gap: 8px;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scan-form-card label {
|
.scan-form-card label {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
margin-bottom: 1px;
|
margin-bottom: 0;
|
||||||
|
margin-top: 0;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
|
text-align: left;
|
||||||
|
padding-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scan-form-card input[type="text"] {
|
.scan-form-card input[type="text"] {
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
margin-bottom: 2px;
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scan-form-card button[type="submit"],
|
||||||
|
.scan-form-card button[type="button"] {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scan-form-card > form > div {
|
||||||
|
grid-column: 1 / -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scan-table-card {
|
.scan-table-card {
|
||||||
|
|||||||
@@ -3,12 +3,15 @@
|
|||||||
{% block title %}Finish Good Scan{% endblock %}
|
{% block title %}Finish Good Scan{% endblock %}
|
||||||
{% block head %}
|
{% block head %}
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/scan.css') }}">
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/scan.css') }}">
|
||||||
|
<!-- QZ Tray for printing -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/qz-tray@2.2.0/qz-tray.min.js"></script>
|
||||||
<style>
|
<style>
|
||||||
.error-message {
|
.error-message {
|
||||||
color: #ff4444;
|
color: #ff4444;
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
display: none;
|
display: none;
|
||||||
|
grid-column: 2 / -1;
|
||||||
}
|
}
|
||||||
.error-message.show {
|
.error-message.show {
|
||||||
display: block;
|
display: block;
|
||||||
@@ -275,6 +278,11 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
} else {
|
} else {
|
||||||
operatorErrorMessage.classList.remove('show');
|
operatorErrorMessage.classList.remove('show');
|
||||||
this.setCustomValidity('');
|
this.setCustomValidity('');
|
||||||
|
|
||||||
|
// Auto-advance when field is complete and valid
|
||||||
|
if (value.length === 4 && value.startsWith('OP')) {
|
||||||
|
cpCodeInput.focus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -312,6 +320,11 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
} else {
|
} else {
|
||||||
cpErrorMessage.classList.remove('show');
|
cpErrorMessage.classList.remove('show');
|
||||||
this.setCustomValidity('');
|
this.setCustomValidity('');
|
||||||
|
|
||||||
|
// Auto-advance when field is complete and valid
|
||||||
|
if (value.length === 15 && value.startsWith('CP')) {
|
||||||
|
oc1CodeInput.focus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -369,6 +382,11 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
} else {
|
} else {
|
||||||
oc1ErrorMessage.classList.remove('show');
|
oc1ErrorMessage.classList.remove('show');
|
||||||
this.setCustomValidity('');
|
this.setCustomValidity('');
|
||||||
|
|
||||||
|
// Auto-advance when field is complete and valid
|
||||||
|
if (value.length === 4 && value.startsWith('OC')) {
|
||||||
|
oc2CodeInput.focus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -423,6 +441,11 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
} else {
|
} else {
|
||||||
oc2ErrorMessage.classList.remove('show');
|
oc2ErrorMessage.classList.remove('show');
|
||||||
this.setCustomValidity('');
|
this.setCustomValidity('');
|
||||||
|
|
||||||
|
// Auto-advance when field is complete and valid
|
||||||
|
if (value.length === 4 && value.startsWith('OC')) {
|
||||||
|
defectCodeInput.focus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -550,6 +573,12 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
localStorage.setItem('fg_scan_last_defect', defectCodeInput.value);
|
localStorage.setItem('fg_scan_last_defect', defectCodeInput.value);
|
||||||
|
|
||||||
// Check if scan-to-boxes is enabled and defect code is 000
|
// Check if scan-to-boxes is enabled and defect code is 000
|
||||||
|
console.log('Auto-submit check:', {
|
||||||
|
scanToBoxesEnabled: scanToBoxesEnabled,
|
||||||
|
defectCode: this.value,
|
||||||
|
shouldShowModal: scanToBoxesEnabled && this.value === '000'
|
||||||
|
});
|
||||||
|
|
||||||
if (scanToBoxesEnabled && this.value === '000') {
|
if (scanToBoxesEnabled && this.value === '000') {
|
||||||
console.log('Auto-submit: Scan-to-boxes enabled, calling submitScanWithBoxAssignment');
|
console.log('Auto-submit: Scan-to-boxes enabled, calling submitScanWithBoxAssignment');
|
||||||
submitScanWithBoxAssignment();
|
submitScanWithBoxAssignment();
|
||||||
@@ -647,6 +676,15 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
alert('✅ Saved Quality Operator code cleared! Please re-enter your operator code.');
|
alert('✅ Saved Quality Operator code cleared! Please re-enter your operator code.');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Initialize QZ Tray for printing box labels
|
||||||
|
if (window.qz) {
|
||||||
|
window.qz.websocket.connect().then(() => {
|
||||||
|
console.log('QZ Tray connected for box label printing');
|
||||||
|
}).catch(err => {
|
||||||
|
console.warn('QZ Tray not available:', err);
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -656,6 +694,10 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
// Quick box creation button
|
// Quick box creation button
|
||||||
document.getElementById('quick-box-create-btn').addEventListener('click', async function() {
|
document.getElementById('quick-box-create-btn').addEventListener('click', async function() {
|
||||||
try {
|
try {
|
||||||
|
this.disabled = true;
|
||||||
|
this.textContent = 'Creating...';
|
||||||
|
|
||||||
|
// Step 1: Create box in database
|
||||||
const createResponse = await fetch('/warehouse/manage_boxes', {
|
const createResponse = await fetch('/warehouse/manage_boxes', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
@@ -670,14 +712,56 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
const result = await createResponse.json();
|
const result = await createResponse.json();
|
||||||
|
|
||||||
if (result.success && result.box_number) {
|
if (result.success && result.box_number) {
|
||||||
await assignCpToBox(result.box_number);
|
const boxNumber = result.box_number;
|
||||||
showNotification(`✅ Box ${result.box_number} created and CP assigned!`, 'success');
|
|
||||||
closeBoxModal();
|
// Step 2: Print the box label using QZ Tray
|
||||||
|
try {
|
||||||
|
// Check QZ Tray connection
|
||||||
|
if (!window.qz || !window.qz.websocket.isActive()) {
|
||||||
|
throw new Error('QZ Tray not connected. Please ensure QZ Tray is running.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get available printers
|
||||||
|
const printers = await window.qz.printers.find();
|
||||||
|
if (printers.length === 0) {
|
||||||
|
throw new Error('No printers found');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use first available printer
|
||||||
|
const printer = printers[0];
|
||||||
|
|
||||||
|
// Create ZPL code for box label
|
||||||
|
const zpl = `^XA
|
||||||
|
^FO50,50^A0N,40,40^FDBox: ${boxNumber}^FS
|
||||||
|
^FO50,120^BY2,3,80^BCN,80,Y,N,N^FD${boxNumber}^FS
|
||||||
|
^XZ`;
|
||||||
|
|
||||||
|
const config = window.qz.configs.create(printer);
|
||||||
|
await window.qz.print(config, [zpl]);
|
||||||
|
|
||||||
|
showNotification(`✅ Box ${boxNumber} created and label printed!`, 'success');
|
||||||
|
|
||||||
|
// Step 3: Keep modal open and focus on scan input
|
||||||
|
document.getElementById('scan-box-input').value = '';
|
||||||
|
document.getElementById('scan-box-input').focus();
|
||||||
|
document.getElementById('scan-box-input').placeholder = 'Scan the printed label now...';
|
||||||
|
|
||||||
|
} catch (printError) {
|
||||||
|
console.error('Print error:', printError);
|
||||||
|
showNotification(`⚠️ Box ${boxNumber} created but print failed: ${printError.message}`, 'warning');
|
||||||
|
// Still keep modal open for manual entry
|
||||||
|
document.getElementById('scan-box-input').focus();
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
throw new Error(result.error || 'Failed to create box');
|
throw new Error(result.error || 'Failed to create box');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showNotification('❌ Error creating box: ' + error.message, 'error');
|
showNotification('❌ Error creating box: ' + error.message, 'error');
|
||||||
|
} finally {
|
||||||
|
// Re-enable button
|
||||||
|
this.disabled = false;
|
||||||
|
this.textContent = 'Quick Box Label Creation';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user