diff --git a/client/blueprints/measure.py b/client/blueprints/measure.py index b0eae1d..5d7bb45 100644 --- a/client/blueprints/measure.py +++ b/client/blueprints/measure.py @@ -124,15 +124,21 @@ def task_execute(task_id: int): # Load all task IDs for this recipe (ordered) for auto-advance recipe_id = task_resp.get("recipe_id") all_task_ids = [] + recipe_resp = {} if recipe_id: tasks_resp = api_client.get(f"/api/recipes/{recipe_id}/tasks") if isinstance(tasks_resp, list): sorted_tasks = sorted(tasks_resp, key=lambda t: t.get("order_index", 0)) all_task_ids = [t["id"] for t in sorted_tasks] + # Fetch recipe for image_path + resp = api_client.get(f"/api/recipes/{recipe_id}") + if not (isinstance(resp, dict) and resp.get("error")): + recipe_resp = resp return render_template( "measure/task_execute.html", task=task_resp, + recipe=recipe_resp, lot_number=lot_number, serial_number=serial_number, all_task_ids=all_task_ids, diff --git a/client/templates/measure/task_execute.html b/client/templates/measure/task_execute.html index 956d4cb..60700e7 100644 --- a/client/templates/measure/task_execute.html +++ b/client/templates/measure/task_execute.html @@ -3,13 +3,12 @@ {% block extra_head %} {% endblock %} {% block content %} -
{# ================================================================ - HEADER — Task info + traceability + HEADER — Compact task info bar ================================================================ #} -
-
-
+
+
- {# Left: Task title and directive #} -
- {# Breadcrumb line #} -
- - - - - {{ _('Task') }} - - - - - - Task {{ (task.order_index or 0) + 1 }} - -
+ {# Back button #} + + + + + -

- {{ task.title or _('Task di misurazione') }} -

+ {# Task badge + title #} +
+ + Task {{ (task.order_index or 0) + 1 }} + +

+ {{ task.title or _('Task di misurazione') }} +

+
- {% if task.directive or task.description %} -

- {{ task.directive or task.description }} -

- {% endif %} + {# Lot + Serial badges #} +
+ {% if lot_number %} +
+ + + + {{ lot_number }}
- - {# Right: Traceability + Caliper #} -
- {# Lot badge #} - {% if lot_number %} -
- - - - {{ lot_number }} -
- {% endif %} - - {# Serial badge #} - {% if serial_number %} -
- - - - {{ serial_number }} -
- {% endif %} - - {# Caliper status #} - {% include "components/caliper_status.html" %} + {% endif %} + {% if serial_number %} +
+ + + + {{ serial_number }}
+ {% endif %} + + {# Caliper status #} + {% include "components/caliper_status.html" %}
{# ================================================================ - MAIN CONTENT — Split layout + MAIN — 3-column layout ================================================================ #} -
-
+
- {# ────────────────────────────────────────────── - LEFT COLUMN — Annotation Viewer (60%) - ────────────────────────────────────────────── #} -
+ {# ────────────────────────────────────────────── + LEFT SIDEBAR — Marker list (vertical) + ────────────────────────────────────────────── #} + + + {# ────────────────────────────────────────────── + CENTER — Image area + ────────────────────────────────────────────── #} +
+ + {# Recipe image strip (small, top, optional) #} +
+ {{ _('Immagine ricetta') }} +
+ + {# Main image area #} +
+ + {# Option A: Subtask has its own image → plain #} +
+ {{ _('Immagine dettaglio misura') }} +
+ + {# Option B: Task image with annotations → annotation-viewer (kept in DOM via x-show) #} +
+ {% if task.file_path %} +
+ +
+ {% endif %} +
+ + {# Option C: No image → placeholder #} +
+
+ + + +

{{ _('Nessuna immagine allegata') }}

+
+
+
+
+ + {# ────────────────────────────────────────────── + RIGHT PANEL — Info + tolerances + numpad + ────────────────────────────────────────────── #} +
+ + {# ---- Subtask header ---- #} +
+
+ +
+

+ +
+ + / + +
+
+ + {# ---- Tolerance parameters (compact grid) ---- #} +
+ {# Nominal row #} +
+ {{ _('Nominale') }} + +
+ + {# 2x2 tolerance grid #} +
+
+ UTL + +
+
+ LTL + +
+
+ UWL + +
+
+ LWL +
+ {# ---- Tolerance bar (compact) ---- #} +
+
+ {# Zone labels #} +
+ + + +
+ {# Center line #} +
+ {# Value needle #} +
+
+
+
+
+ + {# ---- Already measured indicator ---- #} +
+
+ + + + {{ _('Registrata') }}: + + +
+
+ + {# ---- Measurement feedback ---- #} +
+ {% include "components/measurement_feedback.html" %} +
+ + {# ---- Numpad ---- #} +
+ {# Saving overlay #} +
+
+ + + + + {{ _('Salvataggio...') }} +
+
+ + {% include "components/numpad.html" %} +
+ + {# ---- Next measurement indicator ---- #} +
+ {% include "components/next_measurement.html" %} +
+ + {# ---- Error message ---- #} +
+
+ + + + +
+
+
{# ================================================================ FOOTER — Progress bar + navigation ================================================================ #} -
-
-
+
+
- {# Left: Back button #} - - - - - - + {# Left: Back #} + + + + + + - {# Center: Progress #} -
-
- {# Text counter #} - - / - - - {# Progress bar #} -
-
-
- - {# Percentage #} - -
+ {# Center: Progress bar #} +
+ + / + +
+
- - {# Right: Complete / next task button #} - +
+ + {# Right: Summary button #} +
@@ -495,7 +429,6 @@ x-transition:enter-start="opacity-0 scale-90" x-transition:enter-end="opacity-100 scale-100"> - {# Success icon #}
@@ -507,7 +440,6 @@ {{ _('Tutte le') }} {{ _('misurazioni sono state registrate.') }}

- {# Summary counts #}
@@ -549,14 +481,16 @@ /** * Task Execute - Main Alpine.js component * Manages measurement workflow state, save logic, and navigation. + * Layout: 3-column (marker sidebar | image center | info+numpad right) */ function taskExecute() { return { // ---- Data from server ---- - task: {{ task|tojson }}, - subtasks: {{ task.subtasks|tojson if task.subtasks else '[]' }}, + task: window.__taskData, + subtasks: window.__taskSubtasks, lotNumber: '{{ lot_number or '' }}', serialNumber: '{{ serial_number or '' }}', + recipeImagePath: window.__recipeImagePath || '', // ---- Measurement state ---- currentIndex: 0, @@ -568,6 +502,17 @@ function taskExecute() { // ---- Value from numpad / caliper ---- currentValue: null, + // ---- Image switching logic ---- + get currentSubtaskImage() { + return this.currentSubtask?.image_path || null; + }, + get showSubtaskImage() { + return !!this.currentSubtaskImage; + }, + get showTaskImage() { + return !this.currentSubtaskImage && !!this.task.file_path; + }, + // ---- Computed properties ---- get currentSubtask() { return this.subtasks[this.currentIndex] || null; @@ -635,7 +580,6 @@ function taskExecute() { // ---- Init ---- init() { - // Sort subtasks by order_index to ensure correct order this.subtasks.sort((a, b) => (a.order_index || 0) - (b.order_index || 0)); }, @@ -661,14 +605,13 @@ function taskExecute() { this.currentValue = value; this.errorMessage = ''; - // Compute pass/fail before saving const pf = this.passFailStatus; const dev = this.deviation; this.saving = true; try { - const csrfToken = document.querySelector('meta[name="csrf-token"]')?.content || ''; + const csrfToken = document.querySelector('meta[name=csrf-token]')?.content || ''; const response = await fetch('{{ url_for("measure.save_measurement") }}', { method: 'POST', @@ -694,7 +637,7 @@ function taskExecute() { return; } - // Record measurement locally (replace if already measured) + // Record measurement locally const existingIdx = this.measurements.findIndex(m => m.subtask_id === this.currentSubtask.id); const mEntry = { subtask_id: this.currentSubtask.id, value, pass_fail: pf, deviation: dev }; if (existingIdx !== -1) { @@ -705,25 +648,21 @@ function taskExecute() { this.saving = false; - // Pause 1s to let user see the result (pass/fail/warning) + // Pause to show result feedback await new Promise(r => setTimeout(r, 1000)); // Check if all done if (this.completedCount >= this.totalSubtasks) { - // Auto-advance to next task, or show overlay on last task const taskIds = window.__allTaskIds || []; const currentIdx = taskIds.indexOf(this.task.id); if (currentIdx >= 0 && currentIdx < taskIds.length - 1) { - // Navigate to next task window.location.href = '{{ url_for("measure.task_execute", task_id=0) }}'.replace('/0', '/' + taskIds[currentIdx + 1]); } else { - // Last task (or unknown): show completion overlay this.showCompletionOverlay = true; } return; } - // Advance to next unmeasured subtask this.advanceToNext(); } catch (err) { @@ -737,15 +676,12 @@ function taskExecute() { advanceToNext() { this.currentValue = null; - // First try the next sequential subtask for (let i = this.currentIndex + 1; i < this.totalSubtasks; i++) { if (!this.isMeasured(this.subtasks[i].id)) { this.currentIndex = i; return; } } - - // Wrap around from beginning for (let i = 0; i < this.currentIndex; i++) { if (!this.isMeasured(this.subtasks[i].id)) { this.currentIndex = i;