fix: make the by-metre product reachable and its declaration verified
Navigation links carried data-modo-cta and opened loose artwork directly, so every entry point except the price card bypassed "Arquivo por metro". Dropping a PNG or JPG from the by-metre editor then switched the order to loose artwork and repriced it, while the same screen advertised PNG/JPG in its drop zone and marked that path as the cheaper one. Replace the guessing with a declaration: the product is the choice. #tipoEnvio shows ready sheet and loose artwork side by side with both prices, in all four modes, reversible until a file is attached and locked afterwards. sel() no longer reassigns modo, so a file can never change product or price on its own. Verify the declaration instead of trusting it. medirFolha returns dpiFolha, and an image without the pixels to span the film width at DPI_RECUSA is refused as a sheet, with the numbers shown and one click to send it as loose artwork. Previously a small image declared as a sheet was billed by the metre. Scope pintaCaminhos to #caminhos .cam: its global selector was clearing the new control. Move the checkout status out of #carr, which the success path hides, so a paid order still confirms itself to the customer. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -103,9 +103,28 @@ try{
|
||||
if(drop)$('zona').dispatchEvent(new DragEvent('drop',{dataTransfer:dt,bubbles:true,cancelable:true}));
|
||||
else{$('inp').files=dt.files;$('inp').dispatchEvent(new Event('change',{bubbles:true}));}
|
||||
};
|
||||
// A genuine mounted sheet spans the film width with printable resolution.
|
||||
window.sendSheet=async()=>{
|
||||
const cm=larguraFilme(), px=Math.ceil(cm/2.54*150);
|
||||
const c=document.createElement('canvas');c.width=px;c.height=Math.round(px*0.6);
|
||||
const ctx=c.getContext('2d');ctx.fillStyle='white';ctx.fillRect(0,0,c.width,c.height);
|
||||
ctx.fillStyle='#222';ctx.fillRect(40,40,px/4,px/4);
|
||||
const blob=await new Promise(r=>c.toBlob(r,'image/png'));
|
||||
const dt=new DataTransfer();dt.items.add(new File([blob],'folha-montada.png',{type:'image/png'}));
|
||||
$('inp').files=dt.files;$('inp').dispatchEvent(new Event('change',{bubbles:true}));
|
||||
};
|
||||
window.pickTipo=t=>document.querySelector('#tipoEnvio .cam[data-tipo="'+t+'"]').click();
|
||||
})()`);
|
||||
// Reproduce the reported entry path, with no navigation workaround.
|
||||
// Navigation links must reach the chooser, never preselect a product.
|
||||
assert.equal(await evaluate(`document.querySelectorAll('[data-modo-cta]').length`),0);
|
||||
await click('[data-modo="file"]');
|
||||
// By-metre opens declaring a mounted sheet; switching is explicit and priced.
|
||||
assert.deepEqual(await evaluate(`({shown:!$('tipoEnvio').hidden,
|
||||
on:[...document.querySelectorAll('#tipoEnvio .cam.on')].map(b=>b.dataset.tipo)})`),
|
||||
{shown:true,on:['folha']});
|
||||
await evaluate(`pickTipo('avulsa')`);
|
||||
assert.equal(await evaluate('modo'),'avulsa');
|
||||
await evaluate('sendImage()');
|
||||
await waitFor(()=>evaluate(`artes.length===1 && !!artes[0].src`),'JPG artwork model');
|
||||
assert.deepEqual(await evaluate(`({mode:modo,sheets:folhas.length,width:artes[0].cm,quantity:artes[0].q})`),{mode:'avulsa',sheets:0,width:0,quantity:1});
|
||||
@@ -134,26 +153,35 @@ try{
|
||||
const m=result.pos[0].m;
|
||||
return !m.bits[2*m.cw+2] && !m.bits[2*m.cw+30] && !!m.bits[15*m.cw+30];
|
||||
})()`),true);
|
||||
// Explicit finished-sheet images retain their separate dimensions and price.
|
||||
// A real finished sheet keeps its own dimensions and by-metre price.
|
||||
await click('#bVoltar');await click('[data-modo="file"]');
|
||||
await click('#imagemComoFolha');await evaluate('sendImage()');
|
||||
await waitFor(()=>evaluate('folhas.length===1 && !!folhas[0].previewSrc && metros===1.14'),'ready-sheet image');
|
||||
await evaluate('sendSheet()');
|
||||
await waitFor(()=>evaluate('folhas.length===1 && !!folhas[0].previewSrc && metros===0.342'),'ready-sheet image');
|
||||
assert.deepEqual(await evaluate(`({mode:modo,art:artes.length,title:$('montcabTit').textContent,rate:precoBase(100)})`),{mode:'file',art:0,title:'Sua folha',rate:14.9});
|
||||
assert.equal(await evaluate(`document.querySelectorAll('[data-cm]').length`),0);
|
||||
// With a sheet loaded the declaration is locked; it cannot flip under the customer.
|
||||
assert.equal(await evaluate(`document.querySelector('#tipoEnvio .cam[data-tipo="avulsa"]').disabled`),true);
|
||||
await evaluate(`(()=>{const el=$('lista').querySelector('[data-repf]');el.value=2;el.dispatchEvent(new Event('change',{bubbles:true}));})()`);
|
||||
await waitFor(()=>evaluate('metros===2.28'),'ready-sheet repetitions');
|
||||
await waitFor(()=>evaluate('metros===0.684'),'ready-sheet repetitions');
|
||||
assert.equal(await evaluate(`document.querySelectorAll('.folhaPrevia').length`),1);
|
||||
// Keep manual ready-sheet uploads intact and refuse ambiguous mixed batches.
|
||||
// An image too small to span the film is refused, never silently repriced.
|
||||
await click('#bVoltar');await click('[data-modo="file"]');
|
||||
await evaluate('sendImage()');
|
||||
await waitFor(()=>evaluate(`!!document.getElementById('usarAvulsa')`),'ready-sheet refusal');
|
||||
assert.deepEqual(await evaluate(`({mode:modo,sheets:folhas.length,art:artes.length})`),{mode:'file',sheets:0,art:0});
|
||||
await click('#usarAvulsa');
|
||||
await waitFor(()=>evaluate(`modo==='avulsa' && artes.length===1`),'one-click recovery into loose artwork');
|
||||
// Keep manual ready-sheet uploads intact.
|
||||
await click('#bVoltar');await click('[data-modo="file"]');
|
||||
await evaluate(`sel([new File(['sheet'],'sheet.cdr'),new File(['image'],'art.png')])`);
|
||||
assert.deepEqual(await evaluate('[folhas.length,artes.length]'),[0,0]);
|
||||
await evaluate(`sel([new File(['sheet'],'sheet.cdr')])`);
|
||||
await waitFor(()=>evaluate(`!!$('lista').querySelector('[data-comp]')`),'manual ready sheet');
|
||||
await evaluate(`(()=>{const el=$('lista').querySelector('[data-comp]');el.value=1.01;el.dispatchEvent(new Event('change',{bubbles:true}));})()`);
|
||||
await waitFor(()=>evaluate('itemAtual?.total===21.89'),'unchanged manual pricing');
|
||||
// PNG drag/drop follows exactly the same artwork path; UV stays UV.
|
||||
for(const [mode,expected] of [['file','avulsa'],['avulsa','avulsa'],['uvfile','uv']]){
|
||||
// PNG drag/drop follows exactly the same artwork path; UV stays UV. Reaching it
|
||||
// from a by-metre product is one declared click, and it is reversible.
|
||||
for(const [mode,expected] of [['file','avulsa'],['avulsa','avulsa'],['uvfile','uv'],['uv','uv']]){
|
||||
await click('#bVoltar');await click('[data-modo="'+mode+'"]');
|
||||
if(await evaluate(`ehFolha()`))await evaluate(`pickTipo('avulsa')`);
|
||||
await evaluate(`sendImage('image/png',true)`);
|
||||
await waitFor(()=>evaluate('artes.length===1 && !!artes[0].src'),'PNG drop');
|
||||
assert.equal(await evaluate('modo'),expected);
|
||||
|
||||
Reference in New Issue
Block a user