feat: FASE 3 - Flusso MeasurementTec (selezione ricetta, esecuzione misure, riepilogo)

Implementazione completa del flusso operativo per il ruolo MeasurementTec:

Blueprint measure.py:
- select_recipe: selezione ricetta con ricerca e barcode
- task_list: lista task con conteggi subtask e allegati
- task_execute: esecuzione misure con numpad, calibro USB, feedback real-time
- task_complete: riepilogo con statistiche pass/fail e export CSV
- API AJAX: lookup-barcode, save-traceability, save-measurement
- Autorizzazione role_required("MeasurementTec") su tutte le route

Componenti riutilizzabili:
- numpad.html/js/css: tastierino numerico touch-friendly con keyboard support
- caliper_status.html + caliper.js: integrazione calibro USB via Web Serial API
- barcode_scanner.html + barcode.js: scansione barcode con html5-qrcode
- measurement_feedback.html: feedback visivo pass/warning/fail in tempo reale
- next_measurement.html: indicatore prossima misurazione
- annotation-viewer.js: visualizzatore canvas con marker su disegni tecnici
- csv-export.js: export CSV con locale italiano (delimitatore ;, decimale ,)

Sicurezza:
- Decoratore role_required(*roles) per autorizzazione basata su ruoli
- CSRF token su tutti i POST AJAX
- |tojson per prevenire XSS su annotations_json
- Validazione input lato client e server

i18n: 23+ nuove chiavi tradotte IT/EN per tutti i template FASE 3

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Adriano
2026-02-07 08:40:58 +01:00
parent edd4580a5a
commit a386986c17
19 changed files with 3905 additions and 16 deletions
@@ -0,0 +1,76 @@
{# Measurement Feedback Component
Mostra feedback visivo in tempo reale della misurazione corrente.
Variabili Alpine.js richieste nel parent component:
- currentValue: float | null - valore corrente misurato
- nominal: float - valore nominale
- utl: float - Upper Tolerance Limit
- uwl: float - Upper Warning Limit
- lwl: float - Lower Warning Limit
- ltl: float - Lower Tolerance Limit
- unit: string - unità di misura (es. "mm")
- passFailStatus: computed - 'pass' | 'warning' | 'fail'
- deviation: computed - scostamento dal nominale
- progressWidth: computed - larghezza barra percentuale
#}
<div class="measurement-feedback" x-show="currentValue !== null">
<!-- Status bar colorata -->
<div class="h-3 rounded-full overflow-hidden bg-slate-200 dark:bg-slate-600">
<div class="h-full rounded-full transition-all duration-300"
:class="{
'bg-measure-pass': passFailStatus === 'pass',
'bg-measure-warning': passFailStatus === 'warning',
'bg-measure-fail': passFailStatus === 'fail'
}"
:style="'width: ' + progressWidth + '%'">
</div>
</div>
<!-- Status text + deviation -->
<div class="flex justify-between items-center mt-2">
<div class="flex items-center gap-2">
<!-- Icona stato - CONFORME -->
<template x-if="passFailStatus === 'pass'">
<span class="flex items-center gap-1 text-measure-pass font-semibold text-sm">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M5 13l4 4L19 7"/>
</svg>
{{ _('CONFORME') }}
</span>
</template>
<!-- Icona stato - ATTENZIONE -->
<template x-if="passFailStatus === 'warning'">
<span class="flex items-center gap-1 text-measure-warning font-semibold text-sm">
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
<path fill-rule="evenodd" d="M9.401 3.003c1.155-2 4.043-2 5.197 0l7.355 12.748c1.154 2-.29 4.5-2.599 4.5H4.645c-2.309 0-3.752-2.5-2.598-4.5L9.4 3.003zM12 8.25a.75.75 0 01.75.75v3.75a.75.75 0 01-1.5 0V9a.75.75 0 01.75-.75zm0 8.25a.75.75 0 100-1.5.75.75 0 000 1.5z" clip-rule="evenodd"/>
</svg>
{{ _('ATTENZIONE') }}
</span>
</template>
<!-- Icona stato - NON CONFORME -->
<template x-if="passFailStatus === 'fail'">
<span class="flex items-center gap-1 text-measure-fail font-semibold text-sm">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M6 18L18 6M6 6l12 12"/>
</svg>
{{ _('NON CONFORME') }}
</span>
</template>
</div>
<!-- Deviation con segno -->
<span class="font-mono text-sm font-medium"
:class="{
'text-measure-pass': passFailStatus === 'pass',
'text-measure-warning': passFailStatus === 'warning',
'text-measure-fail': passFailStatus === 'fail'
}"
x-text="deviation !== null ?
(deviation >= 0 ? '+' : '') + deviation.toFixed(3) + ' ' + unit : ''">
</span>
</div>
</div>