feat: Kanban shows each order's artwork, filters quotes and searches orders and quotes
All checks were successful
Build and deploy / Validate source (push) Successful in 1m25s
Build and deploy / Integration suite on a real stack (push) Successful in 3m16s
Build and deploy / Secret scan and release gate (push) Successful in 13s
Build and deploy / Publish images (push) Successful in 1m29s
All checks were successful
Build and deploy / Validate source (push) Successful in 1m25s
Build and deploy / Integration suite on a real stack (push) Successful in 3m16s
Build and deploy / Secret scan and release gate (push) Successful in 13s
Build and deploy / Publish images (push) Successful in 1m29s
The customer's browser sends the small picture it already makes of each file (at most 300 KB, WebP/JPEG/PNG read from the bytes, own uploads only); it goes when the file's bytes go. Board cards, quote rows, the order panel and the quote detail show it, with the layout drawing as a second view and as the fallback for files without a picture. Cotações gets a customer filter (e-mail, CNPJ, WhatsApp) and product, layout and resolution-warning chips, with the page bar always shown. The board adds Entrega, Retirada and stalled-order chips. The top search now covers orders in any stage and unpaid quotes; CNPJ and phone match by digits only when nothing but digits and punctuation was typed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -118,6 +118,22 @@
|
||||
}
|
||||
return e.promise;
|
||||
}
|
||||
// The Kanban shows each order's artwork from the small picture this page
|
||||
// already made of it (site-upload.js). Never blocks the order: a picture that
|
||||
// fails to go leaves the Kanban with the layout drawing it shows today.
|
||||
const fotosEnviadas=new Set();
|
||||
function enviaFoto(uid, foto) {
|
||||
if (!uid || !foto || fotosEnviadas.has(uid)) return;
|
||||
fotosEnviadas.add(uid);
|
||||
try {
|
||||
const [cabecalho, dados] = foto.split(',');
|
||||
const bin = atob(dados), bytes = new Uint8Array(bin.length);
|
||||
for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
|
||||
fetch('/api/uploads/'+uid+'/thumbnail', {method:'PUT', body:bytes,
|
||||
headers:{'Content-Type':cabecalho.slice(5).split(';')[0]}})
|
||||
.then(r=>{ if (!r.ok) fotosEnviadas.delete(uid); }, ()=>fotosEnviadas.delete(uid));
|
||||
} catch (e) { fotosEnviadas.delete(uid); }
|
||||
}
|
||||
// A failed upload is shown and waits for the customer, never retried behind
|
||||
// their back: a file the check refused would fail again on every change.
|
||||
const falhas=new Map();
|
||||
@@ -265,7 +281,10 @@
|
||||
for (const item of cart) {
|
||||
if (!item.localFiles?.length) throw new Error('Selecione novamente os arquivos deste item.');
|
||||
const uploads=[];
|
||||
for (const file of item.localFiles) uploads.push(await upload(file));
|
||||
for (const [index,file] of item.localFiles.entries()) {
|
||||
uploads.push(await upload(file));
|
||||
enviaFoto(uploads[index], item.fotos?.[index]);
|
||||
}
|
||||
if (item.production?.version!==2 || item.production.sources?.length!==uploads.length)
|
||||
throw new Error('A montagem deste item precisa ser refeita antes da cotação.');
|
||||
items.push({mode:item.modo, metres:String(item.metros), grade:item.nota, uploads,
|
||||
|
||||
Reference in New Issue
Block a user