e640c4a36e
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
81 lines
2.9 KiB
JavaScript
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 || []); }
|
|
};
|
|
}
|