feat(measure): l'operatore entra nella sequenza, non in un elenco
Punto 10. Sceglere una ricetta apriva la lista completa dei task e chiedeva all'operatore di decidere da dove cominciare prima ancora di aver fatto qualcosa. Ora AVVIA IN SEQUENZA porta dentro il primo task; la lista resta, un livello sotto, per quando serve vederla tutta o tornare indietro. - nuova rotta /measure/start/<ricetta>: memorizza lotto e seriale (prima lo faceva la lista, che ora si salta), verifica la tracciabilità obbligatoria e apre il primo task - GET /api/measurements/task-progress: quante quote ha già preso ogni task di misura, contate per quota e non per tentativo, delimitate dalla produzione aperta o, fuori produzione, dall'operatore - la lista distingue «Incompiuto 1/3» da «Completato 3/3»: un task lasciato a metà non somigliava più a uno mai aperto - «Visualizza singolo TASK» non è più riservato al Maker — dire «incompiuto» senza dare la strada per tornarci sarebbe una lamentela, non una funzione - nel task «Riepilogo» diventa «Completato» e si apre solo quando il ciclo è chiuso: altrimenti sarebbe la scorciatoia che rende facoltative le quote - «Fine ciclo misura» si vede da subito, spento, e dice quante quote mancano; prima compariva a task già finito, quando non serviva più saperlo Il pulsante verde del footer diventa «Task successivo»: due bottoni con la stessa parola addosso sono uno di troppo. Test: 334 (erano 309). Il side effect dei mock del client Flask ora risponde per endpoint invece che per turno — una lista posizionale si rompe appena una pagina fa una domanda in più al server, che è come crescono tutte. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -96,7 +96,16 @@
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
{# Lista task + Riepilogo buttons #}
|
||||
{#
|
||||
Lista task + Completato.
|
||||
|
||||
The list is the second level now: the operator arrives inside the sequence
|
||||
and comes here when they want the whole of it. What used to sit beside it
|
||||
was «Riepilogo», which jumped out of the sequence to a recap - the wrong
|
||||
thing to offer someone halfway through a task. The way forward is
|
||||
«Completato», and on a measurement task it stays shut until the cycle is
|
||||
closed, which is the same rule the footer enforces.
|
||||
#}
|
||||
<div class="shrink-0 flex items-center gap-1.5">
|
||||
<a href="{{ url_for('measure.task_list', recipe_id=task.recipe_id or 0) }}"
|
||||
class="btn btn-secondary text-xs py-1 px-2.5 gap-1">
|
||||
@@ -105,12 +114,15 @@
|
||||
</svg>
|
||||
{{ _('Lista task') }}
|
||||
</a>
|
||||
<button @click="goToSummary()"
|
||||
<button @click="goToNextTask()"
|
||||
:disabled="!canLeaveTask"
|
||||
:title="canLeaveTask ? '' : blockedReason"
|
||||
:class="canLeaveTask ? '' : 'opacity-40 cursor-not-allowed'"
|
||||
class="btn btn-secondary text-xs py-1 px-2.5 gap-1">
|
||||
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 17v-2m3 2v-4m3 4v-6m2 10H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/>
|
||||
</svg>
|
||||
{{ _('Riepilogo') }}
|
||||
{{ _('Completato') }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -630,18 +642,30 @@
|
||||
x-text="Math.round(progressPercent) + '%'"></span>
|
||||
</div>
|
||||
|
||||
{# Right: Fine ciclo misura (measurement tasks) / Completato (non-measurement or after cycle) #}
|
||||
{# Right: Fine ciclo misura (measurement tasks) / Task successivo (after cycle) #}
|
||||
<template x-if="isMeasureTask && subtasks.length > 0">
|
||||
<div class="flex items-center gap-1.5">
|
||||
<button x-show="isComplete && !cycleConfirmed"
|
||||
{#
|
||||
Shown from the start, and greyed until every quote has a value. It used
|
||||
to appear only once the task was finished, which left the operator with
|
||||
no sign that the button existed or what would bring it out - a control
|
||||
that is missing teaches nothing, one that is disabled says what is left.
|
||||
#}
|
||||
<button x-show="!cycleConfirmed"
|
||||
x-transition
|
||||
@click="confirmCycle()"
|
||||
class="btn text-xs shrink-0 gap-1 py-1.5 px-3 shadow-md
|
||||
bg-primary text-white hover:bg-primary-700">
|
||||
:disabled="!isComplete"
|
||||
:title="isComplete ? '' : missingQuotesLabel"
|
||||
:class="isComplete
|
||||
? 'bg-primary text-white hover:bg-primary-700 shadow-md'
|
||||
: 'bg-steel-200 dark:bg-steel-700 text-steel-500 dark:text-steel-400 cursor-not-allowed'"
|
||||
class="btn text-xs shrink-0 gap-1 py-1.5 px-3">
|
||||
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
{{ _('Fine ciclo misura') }}
|
||||
<span x-text="isComplete
|
||||
? {{ _('Fine ciclo misura')|tojson_attr }}
|
||||
: missingQuotesLabel"></span>
|
||||
</button>
|
||||
{# Girare il pezzo e rimisurare, senza chiudere il ciclo: a second reading
|
||||
of the same part is not a new one, and must not restart the interval. #}
|
||||
@@ -654,6 +678,9 @@
|
||||
</svg>
|
||||
{{ _('Rimisura') }}
|
||||
</button>
|
||||
{# The cycle is closed; this only walks on. «Completato» is the header's
|
||||
word for finishing a task, and two buttons wearing it would be one too
|
||||
many. #}
|
||||
<button x-show="cycleConfirmed"
|
||||
x-transition
|
||||
@click="goToNextTask()"
|
||||
@@ -662,7 +689,7 @@
|
||||
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/>
|
||||
</svg>
|
||||
{{ _('Completato') }}
|
||||
{{ _('Task successivo') }}
|
||||
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M14 5l7 7m0 0l-7 7m7-7H3"/>
|
||||
</svg>
|
||||
@@ -988,6 +1015,40 @@ function taskExecute() {
|
||||
: 0;
|
||||
},
|
||||
|
||||
/* How many quotes are still owed, worded rather than counted down in silence.
|
||||
This is what the greyed «Fine ciclo misura» says instead of nothing. */
|
||||
get missingQuotes() {
|
||||
return Math.max(0, this.totalSubtasks - this.completedCount);
|
||||
},
|
||||
|
||||
get missingQuotesLabel() {
|
||||
const n = this.missingQuotes;
|
||||
return n === 1
|
||||
? {{ _('Manca 1 quota')|tojson }}
|
||||
: {{ _('Mancano')|tojson }} + ' ' + n + ' ' + {{ _('quote')|tojson }};
|
||||
},
|
||||
|
||||
/* Whether «Completato» can take the operator on.
|
||||
|
||||
A documental task is done when they say it is. A measurement task is done
|
||||
when its cycle is closed - otherwise the button would be a way round «Fine
|
||||
ciclo misura», and the quotes it exists to collect would be optional. */
|
||||
get canLeaveTask() {
|
||||
if (this.blockedByTolerance) return false;
|
||||
if (this.isMeasureTask && this.subtasks.length > 0) {
|
||||
return this.cycleConfirmed;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
get blockedReason() {
|
||||
if (this.blockedByTolerance) {
|
||||
return {{ _("Serve l'autorizzazione del capoturno")|tojson }};
|
||||
}
|
||||
if (!this.isComplete) return this.missingQuotesLabel;
|
||||
return {{ _('Chiudi prima il ciclo di misura')|tojson }};
|
||||
},
|
||||
|
||||
// ---- Pass/fail logic ----
|
||||
get passFailStatus() {
|
||||
if (this.currentValue === null || !this.currentSubtask) return null;
|
||||
|
||||
Reference in New Issue
Block a user