Files
TieMeasureFlow/src/frontend/flask_app/static/js/vision_recipe.js
T

81 lines
2.9 KiB
JavaScript

// Ricetta visione del Maker (#46): componente Alpine per i task camera_measure / dxf_compare.
// Carica la ricetta VisionSuite v2 + i file allegati verso /maker/api/vision/tasks/<id>/recipe,
// mostra cartella, file e uscite numeriche; notifica le uscite al task editor con un evento.
// Uso: x-data="visionRecipe(task.id, csrfToken, msgs)". Dipende da Alpine.js.
function visionRecipe(taskId, csrf, msgs) {
return {
taskId: taskId,
recipeDir: null,
files: [],
outputs: [],
errors: [], // errori 422 elencati in chiaro
loading: false,
busy: false,
recipeFile: null,
attachments: [],
msgs: msgs || {},
async init() {
// All'apertura: ricetta corrente (GET); il 404/errore non blocca l'editor
this.loading = true;
try {
const resp = await fetch(`/maker/api/vision/tasks/${this.taskId}/recipe`);
if (resp.ok) this._apply(await resp.json());
} catch (e) { console.error('visionRecipe init', e); }
this.loading = false;
},
// Aggiorna lo stato e avvisa il task editor (select vision_output delle quote)
_apply(data) {
this.recipeDir = data.recipe_dir || null;
this.files = data.files || [];
this.outputs = data.outputs || [];
window.dispatchEvent(new CustomEvent('vision-outputs', {
detail: { taskId: this.taskId, outputs: this.outputs }
}));
},
// Il 422 porta `errors` (lista) oppure `detail` (stringa o lista di voci)
_errorsFrom(data) {
if (Array.isArray(data.errors) && data.errors.length) return data.errors.map(String);
const d = data.detail;
if (Array.isArray(d)) return d.map(x => (typeof x === 'string' ? x : (x.msg || JSON.stringify(x))));
if (d && typeof d === 'object') {
if (Array.isArray(d.errors)) return d.errors.map(String);
return [JSON.stringify(d)];
}
return [d || this.msgs.genericError || 'Error'];
},
async upload() {
if (!this.recipeFile) return;
this.busy = true;
this.errors = [];
try {
const fd = new FormData();
fd.append('recipe', this.recipeFile);
for (const f of this.attachments) fd.append('files', f);
const resp = await fetch(`/maker/api/vision/tasks/${this.taskId}/recipe`, {
method: 'POST',
headers: { 'X-CSRFToken': typeof csrf === 'function' ? csrf() : csrf },
body: fd
});
const data = await resp.json();
if (!resp.ok || data.error) {
this.errors = this._errorsFrom(data);
} else {
this._apply(data);
this.recipeFile = null;
this.attachments = [];
}
} catch (e) {
this.errors = [this.msgs.genericError || String(e)];
}
this.busy = false;
},
onRecipe(ev) { this.recipeFile = ev.target.files[0] || null; },
onAttachments(ev) { this.attachments = Array.from(ev.target.files || []); }
};
}