/* Site DTF — The grade shown to the customer, and the reservations attached to it. Extracted verbatim from the single inline script in web/index.html. Loaded as classic scripts in the order listed there: they share one global scope and run top to bottom, exactly as the original did. */ // ── enviar // Avalia sozinho conforme o cliente sobe e preenche. Devolve o que ainda falta, // para a fila dizer em texto em vez de travar num botão. function avaliar(){ if(ehFolha()){ if(!folhas.length) return {pronto:false, falta:'Arraste sua folha montada para começar.'}; const pdfInvalido=folhas.find(x=>x.measurementError); if(pdfInvalido) return {pronto:false, falta:pdfInvalido.f.name+': '+pdfInvalido.measurementError}; const larga=folhas.find(x=>x.med && x.med.larg > larguraFilme()); if(larga) return {pronto:false, falta:'A folha '+larga.f.name+' tem '+n1(larga.med.larg)+ ' cm e não cabe no filme de '+n1(larguraFilme())+' cm.'}; const semMedida=folhas.filter(x=>!(x.m>0)).length; if(semMedida) return {pronto:false, falta:'Informe o comprimento de '+semMedida+ ' folha'+(semMedida>1?'s':'')+'.'}; metros=folhaTotalM(); reencaixado=false; const ans=folhas.map(x=>x.an).filter(Boolean); if(ans.length!==folhas.length){ // A nota precisa cobrir cada folha faturada. Uma folha sem análise não // herda a nota/desconto de outra folha que o navegador conseguiu abrir. const ausentes=folhas.filter(f=>!f.an); const exts=[...new Set(ausentes.map(f=>extDe(f.f.name)))].join(', '); mostraNota([ ['av','Sem nota · '+exts+' sem análise completa', ausentes.length+' de '+folhas.length+' folha(s) precisam de conferência manual'], ['er','Metro pela tabela · '+rs(TABELA[modo]), 'exportando em PNG ou PDF, cai até '+rs(pisoEscada())], ['fix','Alguém abre seu arquivo','a conferência é feita na mão, na sala'] ], 0, folhas.length>1? folhas.length+' folhas' : folhas[0].f.name, larguraFilme()+' cm · '+fmtM(metros)+' m · preço de tabela'); return {pronto:true}; } const dpi=Math.min(...ans.map(a=>a.dpi)); const n=Math.max(6,Math.min(100,Math.round(dpi/DPI_AVISA*100))); const soma=(k)=>ans.reduce((t,a)=>t+a[k],0); const itens=[ ans.every(a=>a.vetor) ? ['ok','Arte vetorial','sem imagem embutida · imprime em qualquer tamanho'] : [dpi>=DPI_AVISA?'ok':dpi>=DPI_RECUSA?'av':'er','Resolução '+dpi+' DPI', ans.some(a=>a.fonteDpi==='imagens') ? 'média por área das imagens dentro do PDF · pior em '+ Math.min(...ans.map(a=>a.res? a.res.pior : a.dpi))+' DPI' : 'resolução do arquivo · arte ampliada antes de exportar não aparece aqui'], ans.some(a=>a.grande) ? ['ok','Arquivo grande','peças, resíduos e fundo conferidos pela equipe'] : null, ans.some(a=>a.grande) ? null : ['ok', soma('artes')+' peças na folha', Math.round(soma('aproveitamento')/ans.length)+'% da folha virou arte'], ans.some(a=>a.grande) ? null : soma('residuos') ? ['er','Resíduo de recorte', soma('residuos')+' ponto'+(soma('residuos')>1?'s':'')+' abaixo de 2 mm · a impressora imprime'] : ['ok','Sem resíduo de recorte','nada solto na folha'], ans.some(a=>a.grande) ? null : soma('encostadas') ? ['fix', soma('encostadas')+' peças a menos de 5 mm', 'se forem artes diferentes, separamos com 5 mm sem custo'] : ['ok','Espaço entre peças','nenhuma abaixo de 6 mm'], ans.some(a=>a.grande) ? null : ans.some(a=>a.fundoChapado) ? ['er','Fundo chapado','a folha está sem transparência'] : ['ok','Fundo transparente','canal alfa conferido'], null ].filter(Boolean); mostraNota(itens, n, folhas.length>1? folhas.length+' folhas' : folhas[0].f.name, larguraFilme()+' cm · '+fmtM(metros)+' m'); return {pronto:true}; } if(!artes.length) return {pronto:false, falta:'Arraste suas artes para começar.'}; if(artes.some(a=>a.lendo!=null)) return {pronto:false, falta:'Gerando a prévia das artes grandes…'}; if(artes.some(a=>a.decodeError || !(a.src||a.semPrevia) || !(a.px>0) || !(a.py>0))) return {pronto:false, falta:'Não conseguimos ler uma das imagens. Exporte-a novamente em PNG ou JPG antes de continuar.'}; if(artes.some(a=>!Number.isFinite(a.cm) || a.cm>larguraFilme())) return {pronto:false, falta:'A largura da arte precisa caber no filme de '+ n1(larguraFilme())+' cm. Reduza a medida solicitada antes de continuar.'}; const sem=artes.filter(a=>!a.cm).length; if(sem) return {pronto:false, falta:'Informe a largura de '+sem+' arte'+(sem>1?'s':'')+ ' para ver a nota e o preço.'}; // metragem = altura da folha depois de montada, com os 5 mm entre peças metros=+(montagemCm/100).toFixed(4); if(!(metros>0)) return {pronto:false, falta:'Montando a folha…'}; const notas=artes.map(a=>Math.max(6,Math.min(100,Math.round(dpiDe(a)/300*100)))); const media=Math.round(notas.reduce((s,n)=>s+n,0)/notas.length), pior=Math.min(...notas); mostraNota([ ['ok','Montagem por nossa conta','5 mm garantidos'], [cls(pior),'Pior arte em '+pior+'%',pior>=85?'todas em boa resolução':'reenviar maior sai mais barato'] ],media,artes.length+' arte'+(artes.length>1?'s':''), 'folha montada por nós · '+fmtM(metros)+' m'); return {pronto:true}; } // redesenha 220 ms depois da última mexida, para não avaliar a cada tecla let tAval=null; function agendaAvaliacao(){ clearTimeout(tAval); tAval=setTimeout(()=>{ // o produto pode ter sido fechado nesses 220 ms (item foi para o carrinho) if(!modo) return; const r=avaliar(); const el=$('aoVivo'); if(!el) return; if(r.pronto){ el.className='aoVivo pronto'; el.innerHTML='Nota e preço calculados. '+ 'Confira abaixo e feche o pedido.'; }else{ el.className='aoVivo'; el.innerHTML=''+escapeHTML(r.falta)+''; limpaPaineis(); // o carrinho só some se o pedido estiver vazio } },220); } function mostraNota(itens,n,titulo,sub){ nota=n; $('qual').classList.add('on'); $('qNome').textContent=titulo; $('qSub').textContent=sub; const semNota = n===0; $('pct').innerHTML = semNota? '—/100' : n+'/100'; const c = semNota? 'var(--fraco)' : n>=85?'var(--verde)':n>=50?'var(--laranja)':'var(--vermelho)'; $('pct').style.color=c; $('qbi').style.width=(semNota?0:n)+'%'; $('qbi').style.background=c; $('qitens').innerHTML=itens.map(i=> '
'+ icone(i[0]==='ok'?'ok':i[0]==='av'?'alerta':i[0]==='fix'?'refazer':'fechar')+''+ '
'+escapeHTML(i[1])+''+escapeHTML(i[2])+'
').join(''); $('qmsg').style.display='none'; ressalvaResolucao(); carrinho(); } // A resolução do arquivo não é a das artes dentro dele: um PDF em 300 DPI // pode ter uma imagem inserida em 180, e é essa que sai serrilhada. function ressalvaResolucao(){ const box=$('ciente'), chk=$('cienteOk'); let baixas=[], geral=300, recusadas=[]; if(ehFolha()){ // Uma régua só: o número que precifica é o número que bloqueia. // Recusa quando a média por área fica abaixo de 96 DPI — a folha inteira não // vale o filme. Se a média passa mas há peças ruins, é ressalva com aceite. folhas.forEach((x,k)=>{ if(!x.an) return; const r=x.an.res, cm=x.med? x.med.larg : larguraFilme(); const nome=(folhas.length>1? (k+1)+'. ':'')+x.f.name; const extra = r ? {pior:r.pior, de:r.total, quantas:r.quantasBaixas, parte:Math.round(r.fracBaixa*100), quantasAviso:r.quantasAviso, parteAviso:Math.round(r.fracAviso*100)} : {}; if(x.an.dpix.an) ? Math.max(...folhas.filter(x=>x.an).map(x=>x.an.dpi)) : 300; }else{ const comTamanho=artes.filter(a=>a.cm>0); recusadas=comTamanho.filter(a=>dpiDe(a)({nome:a.f.name, dpi:Math.round(dpiDe(a)), cm:a.cm})); baixas=comTamanho.filter(a=>dpiDe(a)>=DPI_RECUSA && dpiDe(a)({nome:a.f.name, dpi:Math.round(dpiDe(a)), cm:a.cm})); const todas=comTamanho.map(a=>dpiDe(a)); geral = todas.length? Math.round(Math.max(...todas)) : 300; } // Abaixo de 96 DPI a sala não imprime. Não é desconto, é recusa — // e não existe aceite que destrave. if(recusadas.length){ $('cienteTxt').innerHTML= '

A média da folha ficou abaixo de '+DPI_RECUSA+' DPI. Nesse ponto a peça '+ 'sai serrilhada a ponto de não valer o filme. Reenvie maior ou reduza a largura.

'+ '
'+recusadas.map(b=>'
'+escapeHTML(b.nome)+' — '+ (b.quantas ? 'média de '+b.dpi+' DPI · pior imagem em '+b.pior+' DPI · '+b.quantas+ ' de '+b.de+' imagens abaixo de '+DPI_RECUSA+' DPI, cobrindo '+b.parte+'% da arte' : b.dpi+' DPI em '+n1(b.cm)+' cm · precisa de '+ Math.ceil(DPI_AVISA*b.cm/2.54)+' px de largura')+'
') .join('')+'
'; $('cienteTit').textContent='Resolução abaixo do que imprimimos'; box.className='ciente on recusa'; chk.checked=false; $('qOk').disabled=true; return; } $('cienteTit').textContent='Ressalva de resolução'; box.className='ciente'; if(!baixas.length){ box.classList.remove('on'); chk.checked=false; $('qOk').disabled=false; return; } const menor=baixas.reduce((p,b)=>b.dpis+b.dpi,0)/baixas.length); $('cienteTxt').innerHTML= '

Parte da arte está abaixo de '+DPI_AVISA+' DPI. Essas peças podem sair '+ 'com traço serrilhado, e isso não tem conserto depois de impresso. '+ 'O resto da folha sai normal.

'+ '
'+baixas.map(b=>'
'+escapeHTML(b.nome)+' — '+ (b.quantasAviso ? 'média de '+b.dpi+' DPI · '+b.quantasAviso+' de '+b.de+' imagens abaixo de '+ DPI_AVISA+' DPI, cobrindo '+b.parteAviso+'% da arte'+ (b.quantas? ' · dessas, '+b.quantas+' abaixo de '+DPI_RECUSA+' DPI ('+ b.parte+'% da arte)' : '') : b.dpi+' DPI'+(b.cm? ' em '+n1(b.cm)+' cm':''))+ '
').join('')+'
'; box.className='ciente on'; chk.checked=false; $('qOk').disabled=true; } $('cienteOk').addEventListener('change',e=>{ if($('ciente').classList.contains('recusa')) return; // recusa não se aceita $('qOk').disabled=!e.target.checked; if(itemAtual && itemAtual.qualityStatus==='warning'){ itemAtual.qualityAcknowledged=e.target.checked; pintaEntrega(); } });