task editor: liste Tipo/Unita e tolleranze generali dalle Impostazioni (#47, #48)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-07 12:37:14 +02:00
parent c8dd5eecfd
commit dffb11f9f2
3 changed files with 294 additions and 21 deletions
@@ -767,18 +767,14 @@
<!-- Measurement Type -->
<div>
<label class="tmf-label text-xs">{{ _('Tipo') }}</label>
<!-- #47: lista da Impostazioni (list_measurement_types) o voci di default; un valore salvato fuori lista resta selezionabile -->
<select x-model="newSubtask.measurement_type"
class="tmf-input">
<option value="">-</option>
<option value="linear">{{ _('Lineare') }}</option>
<option value="diameter">{{ _('Diametro') }}</option>
<option value="radius">{{ _('Raggio') }}</option>
<option value="angle">{{ _('Angolo') }}</option>
<option value="roughness">{{ _('Rugosita') }}</option>
<option value="torque">{{ _('Coppia') }}</option>
<option value="force">{{ _('Forza') }}</option>
<option value="weight">{{ _('Peso') }}</option>
<option value="other">{{ _('Altro') }}</option>
<template x-for="o in measurementTypeOptions()" :key="o.value">
<option :value="o.value" :selected="o.value === newSubtask.measurement_type"
x-text="o.label"></option>
</template>
</select>
</div>
<!-- Nominal -->
@@ -786,24 +782,20 @@
<label class="tmf-label text-xs">{{ _('Nominale') }}</label>
<input type="number" step="any"
x-model.number="newSubtask.nominal"
@input="onNominalInput($event)"
class="tmf-input measure-value text-right"
placeholder="0.000">
</div>
<!-- Unit -->
<div>
<label class="tmf-label text-xs">{{ _('Unita') }}</label>
<!-- #47: lista da Impostazioni (list_units) o voci di default; un valore salvato fuori lista resta selezionabile -->
<select x-model="newSubtask.unit"
class="tmf-input">
<option value="mm">mm</option>
<option value="cm">cm</option>
<option value="m">m</option>
<option value="μm">μm</option>
<option value="in">in</option>
<option value="°">°</option>
<option value="g">g</option>
<option value="kg">kg</option>
<option value="N">N</option>
<option value="Nm">Nm</option>
<template x-for="o in unitOptions()" :key="o.value">
<option :value="o.value" :selected="o.value === newSubtask.unit"
x-text="o.label"></option>
</template>
</select>
</div>
</div>
@@ -814,6 +806,7 @@
<label class="tmf-label text-xs text-red-500">{{ _('LTL') }}</label>
<input type="number" step="any"
x-model.number="newSubtask.ltl"
@input="tolAuto = false"
class="tmf-input measure-value text-right"
placeholder="{{ _('Lim. Tol. Inf.') }}">
</div>
@@ -835,10 +828,14 @@
<label class="tmf-label text-xs text-red-500">{{ _('UTL') }}</label>
<input type="number" step="any"
x-model.number="newSubtask.utl"
@input="tolAuto = false"
class="tmf-input measure-value text-right"
placeholder="{{ _('Lim. Tol. Sup.') }}">
</div>
</div>
<!-- #48: promemoria delle tolleranze generali (Impostazioni) -->
<p class="text-xs text-[var(--text-muted)] mt-2"
x-show="generalTolText()" x-text="generalTolText()"></p>
<!-- Form Actions -->
<div class="flex items-center gap-2 justify-end mt-4">
@@ -1227,6 +1224,27 @@ function taskEditor() {
// Uscite numeriche della ricetta visione per task (#46), riempite da vision_recipe.js
visionOutputs: {},
// ---- Liste e tolleranze generali dalle Impostazioni (#47, #48); null = non configurato ----
configuredTypes: {{ list_measurement_types|default(none)|tojson }},
configuredUnits: {{ list_units|default(none)|tojson }},
generalTolPlus: {{ general_tolerance_plus|default(none)|tojson }},
generalTolMinus: {{ general_tolerance_minus|default(none)|tojson }},
// #48: true = utl/ltl si riempiono da soli dal nominale (solo quota nuova, finche' non li tocca l'utente)
tolAuto: false,
// Voci scritte a mano usate quando la lista non e' configurata (#47)
defaultTypeOptions: [
{value: 'linear', label: {{ _('Lineare')|tojson }}},
{value: 'diameter', label: {{ _('Diametro')|tojson }}},
{value: 'radius', label: {{ _('Raggio')|tojson }}},
{value: 'angle', label: {{ _('Angolo')|tojson }}},
{value: 'roughness', label: {{ _('Rugosita')|tojson }}},
{value: 'torque', label: {{ _('Coppia')|tojson }}},
{value: 'force', label: {{ _('Forza')|tojson }}},
{value: 'weight', label: {{ _('Peso')|tojson }}},
{value: 'other', label: {{ _('Altro')|tojson }}}
],
defaultUnits: ['mm', 'cm', 'm', '\u03bcm', 'in', '\u00b0', 'g', 'kg', 'N', 'Nm'],
// ---- Delete modals ----
deleteTaskModal: false,
deleteTargetTask: null,
@@ -1698,8 +1716,61 @@ function taskEditor() {
return Math.max(...task.subtasks.map(s => s.marker_number || 0)) + 1;
},
// #47: opzioni della select Tipo; il valore corrente fuori lista viene aggiunto per non perderlo
measurementTypeOptions() {
const known = v => (this.defaultTypeOptions.find(d => d.value === v) || {label: v}).label;
const opts = this.configuredTypes
? this.configuredTypes.map(v => ({value: v, label: known(v)})) // un tipo di default noto mantiene l'etichetta tradotta
: this.defaultTypeOptions.slice();
const cur = this.newSubtask.measurement_type;
if (cur && !opts.some(o => o.value === cur)) opts.push({value: cur, label: known(cur)});
return opts;
},
// #47: opzioni della select Unita, stessa logica del Tipo
unitOptions() {
const opts = (this.configuredUnits || this.defaultUnits).map(v => ({value: v, label: v}));
const cur = this.newSubtask.unit;
if (cur && !opts.some(o => o.value === cur)) opts.push({value: cur, label: cur});
return opts;
},
// #47: unita di partenza di una quota nuova: la prima della lista configurata, altrimenti mm
defaultUnit() {
return this.configuredUnits ? this.configuredUnits[0] : 'mm';
},
// #48: testo "Tolleranze generali: +x / -y" sotto i campi; vuoto se non configurate
generalTolText() {
const parts = [];
if (this.generalTolPlus !== null) parts.push('+' + this.generalTolPlus);
if (this.generalTolMinus !== null) parts.push('\u2212' + this.generalTolMinus);
return parts.length ? {{ _('Tolleranze generali')|tojson }} + ': ' + parts.join(' / ') : '';
},
// #48: su quota nuova, scrivendo il nominale riempie utl = nominale + plus e ltl = nominale - minus
onNominalInput(ev) {
if (!this.tolAuto || this.editingSubtaskId) return;
const raw = String(ev.target.value).trim();
const nominal = parseFloat(raw);
if (raw === '' || !isFinite(nominal)) {
// nominale svuotato: i limiti riempiti da noi tornano vuoti
this.newSubtask.utl = null;
this.newSubtask.ltl = null;
return;
}
// decimali: quelli del nominale o delle tolleranze generali, al massimo 4
const dec = s => (String(s).split('.')[1] || '').length;
const decimals = Math.min(4, Math.max(dec(raw), dec(this.generalTolPlus), dec(this.generalTolMinus)));
const round = v => Number(v.toFixed(decimals));
if (this.generalTolPlus !== null) this.newSubtask.utl = round(nominal + this.generalTolPlus);
if (this.generalTolMinus !== null) this.newSubtask.ltl = round(nominal - this.generalTolMinus);
},
resetNewSubtask(taskId) {
const task = this.tasks.find(t => t.id === taskId);
// #48: automatismo attivo solo se c'e' almeno una tolleranza generale
this.tolAuto = this.generalTolPlus !== null || this.generalTolMinus !== null;
this.newSubtask = {
marker_number: task ? this.nextMarkerNumber(task) : 1,
description: '',
@@ -1709,7 +1780,7 @@ function taskEditor() {
uwl: null,
lwl: null,
ltl: null,
unit: 'mm',
unit: this.defaultUnit(), // #47
vision_output: ''
};
},
@@ -1779,6 +1850,7 @@ function taskEditor() {
// Open the same form used for adding, but pre-filled with existing data
this.editingSubtaskId = subtask.id;
this.addingSubtaskForTask = taskId;
this.tolAuto = false; // #48: una quota esistente non viene mai ricalcolata
this.newSubtask = {
marker_number: subtask.marker_number,
description: subtask.description || '',