feat: Major system improvements and production deployment
✨ New Features: - Added view_orders route with proper table display - Implemented CSV upload with preview workflow and date parsing - Added production WSGI server configuration with Gunicorn - Created comprehensive production management scripts 🔧 Bug Fixes: - Fixed upload_data route column mapping for actual CSV structure - Resolved print module database queries and template rendering - Fixed view orders navigation routing (was pointing to JSON API) - Corrected barcode display width constraints in print module - Added proper date format parsing for MySQL compatibility 🎨 UI/UX Improvements: - Updated view_orders template with theme-compliant styling - Hidden barcode text in print module preview for cleaner display - Enhanced CSV upload with two-step preview-then-save workflow - Improved error handling and debugging throughout upload process 🚀 Production Infrastructure: - Added Gunicorn WSGI server with proper configuration - Created systemd service for production deployment - Implemented production management scripts (start/stop/status) - Added comprehensive logging setup - Updated requirements.txt with production dependencies 📊 Database & Data: - Enhanced order_for_labels table compatibility - Fixed column mappings for real CSV data structure - Added proper date parsing and validation - Improved error handling with detailed debugging 🔧 Technical Debt: - Reorganized database setup documentation - Added proper error handling throughout upload workflow - Enhanced debugging capabilities for troubleshooting - Improved code organization and documentation
This commit is contained in:
@@ -23,6 +23,80 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
const defectCodeInput = document.getElementById('defect_code');
|
||||
const form = document.getElementById('fg-scan-form');
|
||||
|
||||
// Restore saved operator code from localStorage (only Quality Operator Code)
|
||||
const savedOperatorCode = localStorage.getItem('fg_scan_operator_code');
|
||||
|
||||
if (savedOperatorCode) {
|
||||
operatorCodeInput.value = savedOperatorCode;
|
||||
}
|
||||
|
||||
// Check if we need to clear fields after a successful submission
|
||||
const shouldClearAfterSubmit = localStorage.getItem('fg_scan_clear_after_submit');
|
||||
if (shouldClearAfterSubmit === 'true') {
|
||||
// Clear the flag
|
||||
localStorage.removeItem('fg_scan_clear_after_submit');
|
||||
localStorage.removeItem('fg_scan_last_cp');
|
||||
localStorage.removeItem('fg_scan_last_defect');
|
||||
|
||||
// Clear CP code, OC1, OC2, and defect code for next scan
|
||||
cpCodeInput.value = '';
|
||||
oc1CodeInput.value = '';
|
||||
oc2CodeInput.value = '';
|
||||
defectCodeInput.value = '';
|
||||
|
||||
// Show success indicator
|
||||
setTimeout(function() {
|
||||
// Focus on CP code field for next scan
|
||||
cpCodeInput.focus();
|
||||
|
||||
// Add visual feedback
|
||||
const successIndicator = document.createElement('div');
|
||||
successIndicator.style.cssText = `
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
background: #4CAF50;
|
||||
color: white;
|
||||
padding: 10px 20px;
|
||||
border-radius: 5px;
|
||||
z-index: 1000;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.2);
|
||||
font-weight: bold;
|
||||
`;
|
||||
successIndicator.textContent = '✅ Scan recorded! Ready for next scan';
|
||||
document.body.appendChild(successIndicator);
|
||||
|
||||
// Remove success indicator after 3 seconds
|
||||
setTimeout(function() {
|
||||
if (successIndicator.parentNode) {
|
||||
successIndicator.parentNode.removeChild(successIndicator);
|
||||
}
|
||||
}, 3000);
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// Focus on the first empty required field (only if not clearing after submit)
|
||||
if (shouldClearAfterSubmit !== 'true') {
|
||||
if (!operatorCodeInput.value) {
|
||||
operatorCodeInput.focus();
|
||||
} else if (!oc1CodeInput.value) {
|
||||
oc1CodeInput.focus();
|
||||
} else if (!oc2CodeInput.value) {
|
||||
oc2CodeInput.focus();
|
||||
} else if (!cpCodeInput.value) {
|
||||
cpCodeInput.focus();
|
||||
} else {
|
||||
defectCodeInput.focus();
|
||||
}
|
||||
}
|
||||
|
||||
// Save operator codes to localStorage when they change (only Quality Operator Code)
|
||||
operatorCodeInput.addEventListener('input', function() {
|
||||
if (this.value.startsWith('OP') && this.value.length >= 3) {
|
||||
localStorage.setItem('fg_scan_operator_code', this.value);
|
||||
}
|
||||
});
|
||||
|
||||
// Create error message element for operator code
|
||||
const operatorErrorMessage = document.createElement('div');
|
||||
operatorErrorMessage.className = 'error-message';
|
||||
@@ -338,6 +412,11 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
const seconds = String(now.getSeconds()).padStart(2, '0');
|
||||
timeInput.value = `${hours}:${minutes}:${seconds}`;
|
||||
|
||||
// Save current CP code and defect code to localStorage for clearing after reload
|
||||
localStorage.setItem('fg_scan_clear_after_submit', 'true');
|
||||
localStorage.setItem('fg_scan_last_cp', cpCodeInput.value);
|
||||
localStorage.setItem('fg_scan_last_defect', defectCodeInput.value);
|
||||
|
||||
// Submit the form
|
||||
form.submit();
|
||||
}
|
||||
@@ -399,6 +478,27 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Add functionality for clear saved codes button
|
||||
const clearSavedBtn = document.getElementById('clear-saved-btn');
|
||||
clearSavedBtn.addEventListener('click', function() {
|
||||
if (confirm('Clear saved Quality Operator code? You will need to re-enter it.')) {
|
||||
// Clear localStorage (only Quality Operator Code)
|
||||
localStorage.removeItem('fg_scan_operator_code');
|
||||
localStorage.removeItem('fg_scan_clear_after_submit');
|
||||
localStorage.removeItem('fg_scan_last_cp');
|
||||
localStorage.removeItem('fg_scan_last_defect');
|
||||
|
||||
// Clear Quality Operator Code field only
|
||||
operatorCodeInput.value = '';
|
||||
|
||||
// Focus on operator code field
|
||||
operatorCodeInput.focus();
|
||||
|
||||
// Show confirmation
|
||||
alert('✅ Saved Quality Operator code cleared! Please re-enter your operator code.');
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -430,6 +530,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
<input type="text" id="time" name="time" value="{{ now().strftime('%H:%M:%S') }}" readonly>
|
||||
|
||||
<button type="submit" class="btn">Submit</button>
|
||||
<button type="button" class="btn" id="clear-saved-btn" style="background-color: #ff6b6b; margin-left: 10px;">Clear Quality Operator</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<p>Upload new orders or view existing orders and manage label data for printing.</p>
|
||||
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
|
||||
<a href="{{ url_for('main.upload_data') }}" class="btn">Upload Orders</a>
|
||||
<a href="{{ url_for('main.get_unprinted_orders') }}" class="btn">View Orders</a>
|
||||
<a href="{{ url_for('main.view_orders') }}" class="btn">View Orders</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -10,8 +10,9 @@
|
||||
|
||||
/* Enhanced table styling */
|
||||
.card.scan-table-card table.print-module-table.scan-table thead th {
|
||||
border-bottom: 2px solid #dee2e6 !important;
|
||||
background-color: #f8f9fa !important;
|
||||
border-bottom: 2px solid var(--app-border-color, #dee2e6) !important;
|
||||
background-color: var(--app-table-header-bg, #2a3441) !important;
|
||||
color: var(--app-text-color, #ffffff) !important;
|
||||
padding: 0.25rem 0.4rem !important;
|
||||
text-align: left !important;
|
||||
font-weight: 600 !important;
|
||||
@@ -22,13 +23,21 @@
|
||||
.card.scan-table-card table.print-module-table.scan-table {
|
||||
width: 100% !important;
|
||||
border-collapse: collapse !important;
|
||||
background-color: var(--app-card-bg, #2a3441) !important;
|
||||
}
|
||||
|
||||
.card.scan-table-card table.print-module-table.scan-table tbody tr:hover td {
|
||||
background-color: #f8f9fa !important;
|
||||
background-color: var(--app-hover-bg, #3a4451) !important;
|
||||
cursor: pointer !important;
|
||||
}
|
||||
|
||||
.card.scan-table-card table.print-module-table.scan-table tbody td {
|
||||
background-color: var(--app-card-bg, #2a3441) !important;
|
||||
color: var(--app-text-color, #ffffff) !important;
|
||||
border: 1px solid var(--app-border-color, #495057) !important;
|
||||
padding: 0.25rem 0.4rem !important;
|
||||
}
|
||||
|
||||
.card.scan-table-card table.print-module-table.scan-table tbody tr.selected td {
|
||||
background-color: #007bff !important;
|
||||
color: white !important;
|
||||
@@ -140,13 +149,13 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Barcode Frame - positioned 10px below rectangle, centered, 90% of label width -->
|
||||
<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;">
|
||||
<!-- Barcode Frame - positioned 10px below rectangle, centered, constrained to label width -->
|
||||
<div id="barcode-frame" style="position: absolute; top: 395px; left: 50%; transform: translateX(-50%); width: 220px; max-width: 220px; height: 50px; background: white; display: flex; flex-direction: column; align-items: center; justify-content: center; overflow: hidden;">
|
||||
<!-- Code 128 Barcode representation -->
|
||||
<svg id="barcode-display" style="width: 100%; height: 40px;"></svg>
|
||||
<svg id="barcode-display" style="width: 100%; height: 40px; max-width: 220px;"></svg>
|
||||
|
||||
<!-- 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 below the bars (hidden in preview) -->
|
||||
<div id="barcode-text" style="font-size: 8px; font-family: 'Courier New', monospace; margin-top: 2px; text-align: center; font-weight: bold; display: none;">
|
||||
<!-- Barcode text will be populated here -->
|
||||
</div>
|
||||
</div>
|
||||
@@ -156,8 +165,8 @@
|
||||
<!-- Vertical Code 128 Barcode representation -->
|
||||
<svg id="vertical-barcode-display" style="width: 100%; height: 35px;"></svg>
|
||||
|
||||
<!-- Vertical barcode text -->
|
||||
<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 (hidden in preview) -->
|
||||
<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%; display: none;">
|
||||
<!-- Vertical barcode text will be populated here -->
|
||||
</div>
|
||||
</div>
|
||||
@@ -475,10 +484,12 @@ function updateLabelPreview(order) {
|
||||
|
||||
JsBarcode("#barcode-display", horizontalBarcodeData, {
|
||||
format: "CODE128",
|
||||
width: 2,
|
||||
width: 1.2,
|
||||
height: 40,
|
||||
displayValue: false,
|
||||
margin: 2
|
||||
margin: 0,
|
||||
fontSize: 0,
|
||||
textMargin: 0
|
||||
});
|
||||
console.log('✅ Horizontal barcode generated successfully');
|
||||
} catch (e) {
|
||||
|
||||
@@ -22,6 +22,74 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
const defectCodeInput = document.getElementById('defect_code');
|
||||
const form = document.querySelector('.form-centered');
|
||||
|
||||
// Load saved operator codes from localStorage (only Quality Operator Code)
|
||||
function loadSavedCodes() {
|
||||
const savedOperatorCode = localStorage.getItem('scan_operator_code');
|
||||
|
||||
if (savedOperatorCode) {
|
||||
operatorCodeInput.value = savedOperatorCode;
|
||||
}
|
||||
}
|
||||
|
||||
// Save operator codes to localStorage (only Quality Operator Code)
|
||||
function saveCodes() {
|
||||
if (operatorCodeInput.value.startsWith('OP')) {
|
||||
localStorage.setItem('scan_operator_code', operatorCodeInput.value);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear saved codes from localStorage (only Quality Operator Code)
|
||||
function clearSavedCodes() {
|
||||
localStorage.removeItem('scan_operator_code');
|
||||
operatorCodeInput.value = '';
|
||||
showSuccessMessage('Quality Operator code cleared!');
|
||||
operatorCodeInput.focus();
|
||||
}
|
||||
|
||||
// Show success message
|
||||
function showSuccessMessage(message) {
|
||||
const successDiv = document.createElement('div');
|
||||
successDiv.className = 'success-message';
|
||||
successDiv.textContent = message;
|
||||
successDiv.style.cssText = `
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
background: #4CAF50;
|
||||
color: white;
|
||||
padding: 15px 20px;
|
||||
border-radius: 4px;
|
||||
z-index: 1000;
|
||||
font-weight: bold;
|
||||
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
|
||||
`;
|
||||
document.body.appendChild(successDiv);
|
||||
|
||||
setTimeout(() => {
|
||||
if (document.body.contains(successDiv)) {
|
||||
document.body.removeChild(successDiv);
|
||||
}
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// Load saved codes on page load
|
||||
loadSavedCodes();
|
||||
|
||||
// Focus on the first empty field
|
||||
setTimeout(() => {
|
||||
if (!operatorCodeInput.value) {
|
||||
operatorCodeInput.focus();
|
||||
} else if (!cpCodeInput.value) {
|
||||
cpCodeInput.focus();
|
||||
} else if (!oc1CodeInput.value) {
|
||||
oc1CodeInput.focus();
|
||||
} else if (!oc2CodeInput.value) {
|
||||
oc2CodeInput.focus();
|
||||
} else {
|
||||
defectCodeInput.focus();
|
||||
}
|
||||
}, 100);
|
||||
|
||||
// Create error message element for operator code
|
||||
const operatorErrorMessage = document.createElement('div');
|
||||
operatorErrorMessage.className = 'error-message';
|
||||
@@ -333,8 +401,21 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
const seconds = String(now.getSeconds()).padStart(2, '0');
|
||||
timeInput.value = `${hours}:${minutes}:${seconds}`;
|
||||
|
||||
// Save operator codes before submitting
|
||||
saveCodes();
|
||||
|
||||
// Submit the form
|
||||
form.submit();
|
||||
|
||||
// Clear CP, OC1, OC2, and defect code fields after successful submission
|
||||
setTimeout(() => {
|
||||
cpCodeInput.value = '';
|
||||
oc1CodeInput.value = '';
|
||||
oc2CodeInput.value = '';
|
||||
defectCodeInput.value = '';
|
||||
showSuccessMessage('Scan submitted successfully!');
|
||||
cpCodeInput.focus();
|
||||
}, 100);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -425,6 +506,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
<input type="text" id="time" name="time" value="{{ now().strftime('%H:%M:%S') }}" readonly>
|
||||
|
||||
<button type="submit" class="btn">Submit</button>
|
||||
<button type="button" class="btn btn-secondary" onclick="clearSavedCodes()" style="margin-top: 10px; background-color: #6c757d;">Clear Quality Operator</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -82,15 +82,35 @@ table.view-orders-table.scan-table tbody tr:hover td {
|
||||
<h3>Upload Order Data for Labels</h3>
|
||||
{% endif %}
|
||||
<form method="POST" enctype="multipart/form-data" class="form-centered" id="csv-upload-form">
|
||||
<label for="csv_file">Choose CSV file:</label>
|
||||
{% if leftover_description %}
|
||||
<button type="submit" class="btn btn-danger" name="clear_table" value="1">Clear Table</button>
|
||||
{% elif not orders %}
|
||||
<input type="file" name="csv_file" accept=".csv" required><br>
|
||||
<button type="submit" class="btn">Upload & Review</button>
|
||||
{% if show_preview %}
|
||||
<!-- Show preview controls -->
|
||||
<input type="hidden" name="action" value="save">
|
||||
<label style="font-weight: bold;">Preview of: {{ filename }}</label><br>
|
||||
<p style="color: #666; font-size: 14px; margin: 10px 0;">
|
||||
Showing first 10 rows. Review the data below and click "Save to Database" to confirm.
|
||||
</p>
|
||||
<button type="submit" class="btn" style="background-color: #28a745;">Save to Database</button>
|
||||
<a href="{{ url_for('main.upload_data') }}" class="btn" style="background-color: #6c757d; margin-left: 10px;">Cancel</a>
|
||||
{% else %}
|
||||
<label style="font-weight: bold;">Selected file: {{ session['csv_filename'] if session['csv_filename'] else 'Unknown' }}</label><br>
|
||||
<button type="button" class="btn" onclick="showPopupAndSubmit()">Upload to Database</button>
|
||||
<!-- Show file upload -->
|
||||
<input type="hidden" name="action" value="preview">
|
||||
<label for="file">Choose CSV file:</label>
|
||||
<input type="file" name="file" accept=".csv" required><br>
|
||||
<button type="submit" class="btn">Upload & Preview</button>
|
||||
|
||||
<!-- CSV Format Information -->
|
||||
<div style="margin-top: 20px; padding: 15px; background-color: var(--app-card-bg, #2a3441); border-radius: 5px; border-left: 4px solid var(--app-accent-color, #007bff); color: var(--app-text-color, #ffffff);">
|
||||
<h5 style="margin-top: 0; color: var(--app-accent-color, #007bff);">Expected CSV Format</h5>
|
||||
<p style="margin-bottom: 10px; color: var(--app-text-color, #ffffff);">Your CSV file should contain columns such as:</p>
|
||||
<ul style="margin-bottom: 10px; color: var(--app-text-color, #ffffff);">
|
||||
<li><strong>order_number</strong> - The order/production number</li>
|
||||
<li><strong>quantity</strong> - Number of items</li>
|
||||
<li><strong>warehouse_location</strong> - Storage location</li>
|
||||
</ul>
|
||||
<p style="color: var(--app-secondary-text, #b8c5d1); font-size: 14px; margin-bottom: 0;">
|
||||
Column names are case-insensitive and can have variations like "Order Number", "Quantity", "Location", etc.
|
||||
</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</form>
|
||||
|
||||
@@ -124,108 +144,45 @@ table.view-orders-table.scan-table tbody tr:hover td {
|
||||
</div>
|
||||
|
||||
<!-- Preview Table Card (expandable height, scrollable) -->
|
||||
<div class="card scan-table-card{% if leftover_description %} leftover-table-card{% endif %}" style="margin-bottom: 24px; max-height: 480px; overflow-y: auto;">
|
||||
{% if leftover_description %}
|
||||
<h3>Left over orders</h3>
|
||||
<div class="card scan-table-card" style="margin-bottom: 24px; max-height: 480px; overflow-y: auto;">
|
||||
{% if show_preview %}
|
||||
<h3>CSV Data Preview - {{ filename }}</h3>
|
||||
<table class="scan-table">
|
||||
<thead>
|
||||
<tr>
|
||||
{% for header in headers %}
|
||||
<th>{{ header }}</th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% if preview_data %}
|
||||
{% for row in preview_data %}
|
||||
<tr>
|
||||
{% for header in headers %}
|
||||
<td>{{ row.get(header, '') }}</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<tr><td colspan="{{ headers|length }}" style="text-align:center;">No data to preview</td></tr>
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<h3>Preview Table</h3>
|
||||
<h3>CSV Data Preview</h3>
|
||||
<table class="scan-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Upload a CSV file to see preview</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td style="text-align:center; padding: 40px;">No CSV file uploaded yet. Use the form above to upload and preview your data.</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
<table class="scan-table view-orders-table{% if leftover_description %} leftover-table{% endif %}">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Comanda<br>Productie</th>
|
||||
<th>Cod<br>Articol</th>
|
||||
<th>Descr. Com.<br>Prod</th>
|
||||
<th>Cantitate</th>
|
||||
<th>Data<br>Livrare</th>
|
||||
<th>Dimensiune</th>
|
||||
<th>Com.Achiz.<br>Client</th>
|
||||
<th>Nr.<br>Linie</th>
|
||||
<th>Customer<br>Name</th>
|
||||
<th>Customer<br>Art. Nr.</th>
|
||||
<th>Open<br>Order</th>
|
||||
<th>Line</th>
|
||||
<th>Printed</th>
|
||||
<th>Created</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% if orders %}
|
||||
{% for order in orders %}
|
||||
{% if order and (order.get('comanda_productie', '') or order.get('descr_com_prod', '')) %}
|
||||
<tr>
|
||||
<td>{{ order.get('id', '') }}</td>
|
||||
<td><strong>{{ order.get('comanda_productie', '') }}</strong></td>
|
||||
<td>{{ order.get('cod_articol', '-') }}</td>
|
||||
<td>{{ order.get('descr_com_prod', '') }}</td>
|
||||
<td style="text-align: right; font-weight: 600;">{{ order.get('cantitate', '') }}</td>
|
||||
<td style="text-align: center;">{{ order.get('data_livrare', '') }}</td>
|
||||
<td style="text-align: center;">{{ order.get('dimensiune', '-') }}</td>
|
||||
<td>{{ order.get('com_achiz_client', '-') }}</td>
|
||||
<td style="text-align: right;">{{ order.get('nr_linie_com_client', '-') }}</td>
|
||||
<td>{{ order.get('customer_name', '-') }}</td>
|
||||
<td>{{ order.get('customer_article_number', '-') }}</td>
|
||||
<td>{{ order.get('open_for_order', '-') }}</td>
|
||||
<td style="text-align: right;">{{ order.get('line_number', '-') }}</td>
|
||||
<td style="text-align: center;">
|
||||
{% if order.get('printed_labels', 0) == 1 %}
|
||||
<span style="color: #28a745; font-weight: bold;">✓ Yes</span>
|
||||
{% else %}
|
||||
<span style="color: #dc3545;">✗ No</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td style="font-size: 11px; color: #6c757d;">{{ order.get('created_at', '-') }}</td>
|
||||
</tr>
|
||||
{% if order.error_message %}
|
||||
<tr>
|
||||
<td colspan="15" style="color: #dc3545; font-size: 12px; background: #fff3f3;">
|
||||
<strong>Error:</strong> {{ order.error_message }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<tr><td colspan="15" style="text-align:center;">No CSV file uploaded yet.</td></tr>
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{% if validation_errors or validation_warnings %}
|
||||
{% if not leftover_description %}
|
||||
<div class="card" style="margin-bottom: 24px;">
|
||||
<h4>Validation Results</h4>
|
||||
{% if validation_errors %}
|
||||
<div style="color: #dc3545; margin-bottom: 16px;">
|
||||
<strong>Errors found:</strong>
|
||||
<ul>
|
||||
{% for error in validation_errors %}
|
||||
<li>{{ error }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if validation_warnings %}
|
||||
<div style="color: #ffc107;">
|
||||
<strong>Warnings:</strong>
|
||||
<ul>
|
||||
{% for warning in validation_warnings %}
|
||||
<li>{{ warning }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if report %}
|
||||
<div class="card" style="margin-bottom: 24px;">
|
||||
<h4>Import Report</h4>
|
||||
<p>{{ report }}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
||||
@@ -25,13 +25,14 @@ table.view-orders-table.scan-table thead th {
|
||||
line-height: 1.3 !important;
|
||||
padding: 6px 3px !important;
|
||||
font-size: 11px !important;
|
||||
background-color: #e9ecef !important;
|
||||
background-color: var(--header-bg-color) !important;
|
||||
color: var(--header-text-color) !important;
|
||||
font-weight: bold !important;
|
||||
text-transform: none !important;
|
||||
letter-spacing: 0 !important;
|
||||
overflow: visible !important;
|
||||
box-sizing: border-box !important;
|
||||
border: 1px solid #ddd !important;
|
||||
border: 1px solid var(--border-color) !important;
|
||||
text-overflow: clip !important;
|
||||
position: relative !important;
|
||||
}
|
||||
@@ -41,7 +42,9 @@ table.view-orders-table.scan-table tbody td {
|
||||
padding: 4px 2px !important;
|
||||
font-size: 10px !important;
|
||||
text-align: center !important;
|
||||
border: 1px solid #ddd !important;
|
||||
border: 1px solid var(--border-color) !important;
|
||||
background-color: var(--card-bg-color) !important;
|
||||
color: var(--text-color) !important;
|
||||
white-space: nowrap !important;
|
||||
overflow: hidden !important;
|
||||
text-overflow: ellipsis !important;
|
||||
@@ -68,7 +71,7 @@ table.view-orders-table.scan-table tbody td {
|
||||
|
||||
/* HOVER EFFECTS */
|
||||
table.view-orders-table.scan-table tbody tr:hover td {
|
||||
background-color: #f8f9fa !important;
|
||||
background-color: var(--hover-color) !important;
|
||||
}
|
||||
|
||||
/* COLUMN WIDTH SPECIFICATIONS */
|
||||
|
||||
Reference in New Issue
Block a user