Files
ScanLook/modules/conssheets/templates/conssheets/process_router.html
Javier 363295762a feat: Implement Smart Router workflow with User Input and Duplicate Logic (v0.18.0)
Major update to the scanning engine to support "Pause & Resume" workflows.
The system can now halt execution to ask for user input (e.g. Weight) and resume
processing seamlessly.

Key Changes:
- Backend (Global Actions): Added `OPEN_FORM` action type to pause pipeline and request manual input.
- Backend (Routes): Updated `scan_lot` to handle `extra_data` payloads, allowing the pipeline to resume after user input.
- Backend (Logic): Implemented `confirm_duplicate` gatekeeper to handle "Warn vs Block" logic dynamically.
- Frontend (JS): Added `processSmartScan` to handle router signals (Open Modal, Warn Duplicate).
- Frontend (JS): Added `saveSmartScanData` to send original barcode + new form data back to the engine.
- UI: Fixed modal ID/Name conflicts (forcing use of `name` attribute for DB compatibility).
- UI: Restored missing "Cancel" button to Details Modal.
- Config: Added "User Input" rule type to the Rule Editor.

Ver: 0.18.0
2026-02-09 00:34:41 -06:00

146 lines
6.5 KiB
HTML

{% extends 'base.html' %}
{% block content %}
<div class="container-fluid p-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2>
<i class="fas fa-random me-2"></i>Routing Rules
<small class="text-muted fs-6 ms-2">IFTTT Logic Engine</small>
</h2>
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#addRuleModal">
<i class="fas fa-plus"></i> Add New Rule
</button>
</div>
<div class="card shadow-sm">
<div class="card-header bg-light">
<h5 class="mb-0">Active Rules</h5>
</div>
<div class="card-body p-0">
{% if rules %}
<table class="table table-hover mb-0">
<thead class="table-light">
<tr>
<th style="width: 80px;">Line</th>
<th>Rule Name</th>
<th>Match Pattern (Regex)</th>
<th>Actions</th>
<th>Status</th>
<th class="text-end">Options</th>
</tr>
</thead>
<tbody>
{% for rule in rules %}
<tr>
<td><span class="badge bg-secondary">{{ rule['line_number'] }}</span></td>
<td><strong>{{ rule['rule_name'] }}</strong></td>
<td><code>{{ rule['match_pattern'] }}</code></td>
<td>
<span class="badge bg-info text-dark">JSON Logic</span>
</td>
<td>
{% if rule['is_active'] %}
<span class="badge bg-success">Active</span>
{% else %}
<span class="badge bg-secondary">Archived</span>
{% endif %}
</td>
<td class="text-end">
<a href="{{ url_for('conssheets.edit_router_rule', process_id=process['id'], rule_id=rule['id']) }}" class="btn btn-sm btn-outline-secondary">Edit</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<div class="text-center py-5">
<i class="fas fa-code-branch fa-3x text-muted mb-3"></i>
<p class="lead text-muted">No routing rules defined yet.</p>
<p class="small text-muted">Rules allow you to auto-parse barcodes into multiple fields.</p>
</div>
{% endif %}
</div>
</div>
</div>
<div class="modal fade" id="addRuleModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Add Routing Rule</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<form action="{{ url_for('conssheets.add_router_rule', process_id=process['id']) }}" method="POST">
<div class="modal-body">
<div class="mb-3">
<label class="form-label">Line Number</label>
<input type="number" class="form-control" name="line_number" placeholder="e.g. 10, 20, 30" required>
<div class="form-text">Rules run in order. Leave gaps (10, 20) for future inserts.</div>
</div>
<div class="mb-3">
<label class="form-label">Rule Name</label>
<input type="text" class="form-control" name="rule_name" placeholder="e.g. Parse Data Matrix" required>
</div>
<div class="mb-3">
<label class="form-label">Match Pattern (Regex)</label>
<input type="text" class="form-control" name="match_pattern" placeholder="e.g. ^\d{8}-.*" required>
<div class="form-text">
Use <code>.*</code> to match everything (default/catch-all).
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">Create Rule</button>
</div>
</form>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Elements
const modal = document.getElementById('addRuleModal');
const openBtn = document.querySelector('[data-target="#addRuleModal"]');
const closeBtns = modal.querySelectorAll('[data-dismiss="modal"]');
// Function to open modal
openBtn.addEventListener('click', function(e) {
e.preventDefault();
modal.style.display = 'block';
setTimeout(() => modal.classList.add('show'), 10); // Small delay for transition
document.body.classList.add('modal-open');
// Add dark backdrop
const backdrop = document.createElement('div');
backdrop.className = 'modal-backdrop fade show';
backdrop.id = 'custom-backdrop';
document.body.appendChild(backdrop);
});
// Function to close modal
function closeModal() {
modal.classList.remove('show');
setTimeout(() => {
modal.style.display = 'none';
document.body.classList.remove('modal-open');
const backdrop = document.getElementById('custom-backdrop');
if (backdrop) backdrop.remove();
}, 150); // Wait for transition
}
// Attach close event to all close buttons (X and Cancel)
closeBtns.forEach(btn => btn.addEventListener('click', closeModal));
// Close if clicking outside the modal content
window.addEventListener('click', function(event) {
if (event.target === modal) {
closeModal();
}
});
});
</script>
{% endblock %}