Files
dtf-system/web/site-config.js
2026-09-23 10:40:18 -03:00

108 lines
6.3 KiB
JavaScript

/* Site DTF — Product tables, commercial ladders, shared state and field validation.
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. */
const $=id=>document.getElementById(id);
const rs=v=>'R$ '+v.toFixed(2).replace('.',',');
const fmt=b=>b<1048576?(b/1024).toFixed(0)+' KB':(b/1048576).toFixed(1).replace('.',',')+' MB';
// ── modos
const MODOS={
file: {tit:'Arquivo por metro', lg:'DTF têxtil · 57 cm', larg:57,
preco:'tabela R$ 19,90/m · até R$ 14,90', multi:true, folha:true,
sub:'Sua folha montada. Revisamos, corrigimos e reencaixamos antes de imprimir.',
zt:'Arraste suas folhas montadas',
zs:'uma ou várias · PNG, JPG, TIFF ou PDF · PSD, AI e CDR passam por tratamento'},
avulsa:{tit:'Artes avulsas', lg:'DTF têxtil · 57 cm', larg:57,
preco:'tabela R$ 29,90/m · até R$ 24,90', multi:true,
sub:'Cada arte num arquivo. Montamos a folha com o melhor encaixe.',
zt:'Arraste suas artes aqui',
zs:'PNG ou JPG com fundo transparente · informe a largura de cada uma'},
uvfile:{tit:'Arquivo por metro · UV', lg:'Adesivo UV · 28,5 cm', larg:28.5,
preco:'tabela R$ 85,90/m · até R$ 69,90', multi:true, folha:true,
sub:'Sua folha de UV montada. Revisamos, corrigimos e reencaixamos antes de imprimir.',
zt:'Arraste suas folhas de UV',
zs:'uma ou várias · PNG, JPG, TIFF ou PDF · PSD, AI e CDR passam por tratamento'},
uv: {tit:'Artes avulsas · UV', lg:'Adesivo UV · 28,5 cm', larg:28.5,
preco:'tabela R$ 99,90/m · até R$ 83,90', multi:true,
sub:'Cada arte de UV num arquivo. Montamos a folha com o melhor encaixe.',
zt:'Arraste suas artes de UV',
zs:'PNG ou JPG com fundo transparente · informe a largura de cada arte'}
};
// Quatro escadas. A de artes avulsas e a da folha pronta mais a montagem:
// R$ 10,00 no têxtil, R$ 14,00 no UV, constante em todas as faixas.
const FAIXAS={
file: [[90,14.90],[75,16.20],[60,17.50],[40,18.70],[0,19.90]],
avulsa:[[90,24.90],[75,26.20],[60,27.50],[40,28.70],[0,29.90]],
uvfile:[[90,69.90],[75,73.90],[60,77.90],[40,81.90],[0,85.90]],
uv: [[90,83.90],[75,87.90],[60,91.90],[40,95.90],[0,99.90]]
};
const TABELA={file:19.90, avulsa:29.90, uvfile:85.90, uv:99.90}; // teto de cada escada
const MONTAGEM={avulsa:10.00, uv:14.00}; // o que a montagem soma sobre a folha pronta
const ehFolha=m=>!!MODOS[m||modo].folha;
const pisoEscada=m=>FAIXAS[m||modo][0][1];
const MINIMO_M=1, GAP_CM=0.5, RODAPE_CM=5;
const RENDERIZA=/[.](png|jpe?g|webp|gif|bmp)$/i;
// Folha pronta: dá para medir PNG, JPG e PDF. PSD, AI, TIFF e CDR entram, mas
// o navegador não abre — o cliente informa o comprimento e o arquivo passa por
// tratamento antes de imprimir.
const ACEITA={
folha: /[.](png|jpe?g|tiff?|pdf|psd|psb|ai|cdr)$/i,
avulsa: /[.](png|jpe?g|webp)$/i
};
const MEDE=/[.](png|jpe?g|webp|gif|bmp|pdf)$/i;
const DPI_RECUSA=96, DPI_AVISA=300; // régua dos designers · 02/09/2026
// A especificação do PDF limita a página a 14.400 unidades — 200 pol, 508 cm.
// Passar disso só é válido com UserUnit (PDF 1.6+), que a maioria dos programas
// não escreve: o arquivo sai fora do padrão e só o gerador lê direito.
const PDF_MAX_PT=14400, PT_CM=2.54/72;
const extDe=n=>(n.split('.').pop()||'').toUpperCase();
let CLIENTE='SEU NOME AQUI', PEDIDO='000000';
let modo=null, artes=[], recemChegada=null, metros=0, nota=0, reencaixado=false;
let folhas=[]; // [{f, med, rep, m}] · o cliente pode subir várias
const folhaTotalM=()=>folhas.reduce((t,x)=>t+(x.m||0)*(x.rep||1),0);
// O cliente declara o que está mandando escolhendo o produto, em vez de o site
// adivinhar pelo formato do arquivo. A declaração de folha montada é conferida na
// medição: uma imagem sem pixel para ocupar a largura do filme não é uma folha.
// Cada modo por metro tem o seu par de artes avulsas, e o seletor anda nos dois sentidos.
const PAR={file:'avulsa', avulsa:'file', uvfile:'uv', uv:'uvfile'};
let montagemCm=0; // altura da folha montada pelo motor, em cm
let montagemLayout=null; // posições aprovadas de cada cópia, em cm
const ZOOMS=[1,1.5,2,3,4]; let zoomI=0; // ampliação da montagem ao vivo
let pedido=[]; // itens já fechados · o pagamento é um só
// A escolha de entrega vale para o pedido inteiro, não por item. No Tiny, retirada
// grava forma_envio X, transportador DropStar e frete por conta do remetente — igual
// ao que a expedição já recebe hoje, sem criar processo novo.
let entrega={tipo:'retira', cep:'', valor:0, cotado:true};
let cliente={cnpj:'', zap:'', mail:''};
let caminho='auto'; // o que o cliente diz que vai mandar
// CNPJ com os dois dígitos verificadores · máscara sem conta não vale nada
function cnpjOk(v){
const d=(v||'').replace(/\D/g,'');
if(d.length!==14 || /^(\d)\1{13}$/.test(d)) return false;
const calc=(base,pesos)=>{
const soma=base.split('').reduce((t,n,i)=>t+ +n*pesos[i],0);
const r=soma%11; return r<2? 0 : 11-r;
};
const p1=[5,4,3,2,9,8,7,6,5,4,3,2], p2=[6,...p1];
return calc(d.slice(0,12),p1)===+d[12] && calc(d.slice(0,13),p2)===+d[13];
}
const zapOk=v=>{ const d=(v||'').replace(/\D/g,''); return d.length===10||d.length===11; };
const mailOk=v=>/^[^\s@]+@[^\s@]+\.[a-z]{2,}$/i.test((v||'').trim());
const clienteOk=()=>cnpjOk(cliente.cnpj)&&zapOk(cliente.zap)&&mailOk(cliente.mail);
const larguraFilme=()=>MODOS[modo].larg;
// cobrança proporcional: arredonda para cima em 10 cm · 2,75 m vira 2,80 m
const cobrar=m=>Math.max(MINIMO_M, Math.ceil(m*10)/10);
const fmtM=m=>m.toFixed(2).replace('.',',');
const precoBase=n=>
(FAIXAS[modo].find(f=>n>=f[0])||FAIXAS[modo][FAIXAS[modo].length-1])[1];
const descontoMetragem=()=>0; // sem progressão por quantidade
const cls=n=>n>=85?'ok':n>=50?'av':'er';
const dpiDe=a=>a.cm>0? (a.giro?a.py:a.px)/(a.cm/2.54) : 0;
// girar 90° inverte a proporção · espelhar não muda medida, só o desenho
const propDe=a=>{ const p=a.prop||1; return a.giro? 1/p : p; };
const TAMANHOS={57:[10,15,20,25,28.2], 28.5:[5,8,10,14,28.2]};