diff --git a/app.js b/app.js
new file mode 100644
index 0000000..cc7b0c8
--- /dev/null
+++ b/app.js
@@ -0,0 +1,259 @@
+const SUPABASE_URL = "https://qfjghplxbtogshfjkawx.supabase.co";
+const SUPABASE_KEY = "sb_publishable_rIcKdaflOvJ0DLTJDcOrxA_bpTGG2hA";
+const supabaseClient = window.supabase.createClient(SUPABASE_URL, SUPABASE_KEY);
+
+const STAR_PRODUCTS = [
+ ["20104425", "PANO SCOTT DURAMAX 58X03X08 BRANCO"],
+ ["20092384", "GUARDANAPO SCALA 22X20 - 3600 UN"],
+ ["20104429", "GUARD. SCOTT GRANDHOTEL FD 9X9X50 P"],
+ ["20104430", "GUARD. SCOTT GRANDHOTEL FD 8X9X50 G"],
+ ["20104313", "PANO SCOTT DURAMAX 58X01X24 BRANCO"],
+ ["20104426", "GUARD. SCOTT DIA A DIA FS 6X12X50 P"],
+ ["20104309", "LENCO FAC KLEENEX BLS FT 10X04X72 L4P3"],
+ ["20092383", "GUARDANAPO SCALA 22X20 - CX 4800 UN"],
+ ["20104422", "LENCO FAC KLEENEX BX FD 100X40 L100P80"],
+ ["20092388", "GUARDAPANO NAPS 33X30 - 1800 UN"],
+ ["20104421", "LENCO FAC KLEENEX BX FD 50X60 L60P50"],
+ ["20092386", "GUARDANAPO NAPS 23X21,5 - 3600 UN"],
+ ["20110078", "PANO SCALA 50X01X24 BRANCO"],
+ ["90004710", "LENÇO UMEDECIDO MIMMO 40x24"],
+ ["90004853", "LENCO UMED. NEVE TOQ SEDA 48X24"],
+ ["90005082", "LENCO UMED. NEVE ON THE GO 16X24"],
+ ["90005191", "LENCO UMED. NEVE 48X4X6 L4P3"],
+ ["20091836", "TOAX430 3R 120F 3X6 SCALA PLUS MEGA"],
+ ["20091835", "TOAX430 2R 100F 2X12 SCALA PLUS MPICOTE"],
+ ["20091834", "TOAX430 2R 60F 2X12 SCALA PLUS REG"],
+ ["20111061", "TOAX430 2R 120F 3X6 SCALA WARM UP"]
+];
+
+const tabButtons = document.querySelectorAll(".tab-btn");
+const panels = document.querySelectorAll(".tab-panel");
+const statusEl = document.getElementById("status");
+const resumoImportacaoEl = document.getElementById("resumo-importacao");
+const listaDtsEl = document.getElementById("lista-dts");
+const importarBtnEl = document.getElementById("importar-sap");
+
+const STORAGE_KEY = "dts-store-v2";
+let dtStore = loadStore();
+
+function loadStore() {
+ const raw = localStorage.getItem(STORAGE_KEY);
+ return raw ? JSON.parse(raw) : {};
+}
+
+function saveStore() {
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(dtStore));
+}
+
+function setImportLoading(loading) {
+ importarBtnEl.disabled = loading;
+ importarBtnEl.textContent = loading ? "Processando..." : "Importar/Atualizar linhas SAP";
+ statusEl.classList.toggle("loading", loading);
+}
+
+tabButtons.forEach((btn) => {
+ btn.addEventListener("click", () => {
+ const target = btn.dataset.tab;
+ tabButtons.forEach((b) => b.classList.remove("active"));
+ panels.forEach((p) => p.classList.remove("active"));
+ btn.classList.add("active");
+ document.getElementById(target).classList.add("active");
+ });
+});
+
+function formatDateFromSAP(value) {
+ const clean = String(value || "").trim();
+ if (/^\d{2}\.\d{2}\.\d{4}$/.test(clean)) {
+ const [dd, mm, yyyy] = clean.split(".");
+ return `${yyyy}-${mm}-${dd}`;
+ }
+ return clean;
+}
+
+async function buscarPadraoPorSku(sku) {
+ const { data, error } = await supabaseClient
+ .from("produtos")
+ .select("sku, fardos_por_palete")
+ .eq("sku", sku)
+ .limit(1);
+
+ if (error || !data?.length) return null;
+ return Number(data[0].fardos_por_palete || 0);
+}
+
+async function parseSapLineToItem(line) {
+ const cols = line.split("\t").map((v) => v.trim());
+ if (cols.length < 14) return null;
+
+ const material = cols[6];
+ const qtd = Number(cols[5] || 0);
+ const padraoSupabase = await buscarPadraoPorSku(material);
+ const padrao = Number(padraoSupabase || cols[12] || 0);
+
+ return {
+ codCliente: cols[0],
+ codTransportadora: cols[1],
+ numeroTransporte: cols[2],
+ infoAgenda: cols[3],
+ nomeCliente: cols[4],
+ setorAtividade: cols[7],
+ clientePallet: cols[8],
+ agrupamentoRegional: cols[9],
+ qtdTeoricaConvertida: cols[10],
+ numeroNfe: cols[11],
+ qtdTeorica: cols[12],
+ dataAgendamento: formatDateFromSAP(cols[13]),
+ item: {
+ sku: material,
+ qtd,
+ padrao,
+ pltFechado: padrao > 0 ? Math.floor(qtd / padrao) : 0,
+ fracao: padrao > 0 ? qtd % padrao : qtd
+ }
+ };
+}
+
+async function importarLinhasSAP() {
+ const raw = document.getElementById("sap-linhas").value.trim();
+ if (!raw) {
+ statusEl.textContent = "Cole uma ou mais linhas do SAP.";
+ return;
+ }
+
+ const lines = raw.split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
+ let importadas = 0;
+
+ setImportLoading(true);
+ statusEl.textContent = `Processando ${lines.length} linha(s)...`;
+
+ try {
+ for (const line of lines) {
+ const parsed = await parseSapLineToItem(line);
+ if (!parsed?.numeroTransporte) continue;
+
+ if (!dtStore[parsed.numeroTransporte]) {
+ dtStore[parsed.numeroTransporte] = {
+ codCliente: parsed.codCliente,
+ codTransportadora: parsed.codTransportadora,
+ numeroTransporte: parsed.numeroTransporte,
+ infoAgenda: parsed.infoAgenda,
+ nomeCliente: parsed.nomeCliente,
+ setorAtividade: parsed.setorAtividade,
+ clientePallet: parsed.clientePallet,
+ agrupamentoRegional: parsed.agrupamentoRegional,
+ qtdTeoricaConvertida: parsed.qtdTeoricaConvertida,
+ numeroNfe: parsed.numeroNfe,
+ qtdTeorica: parsed.qtdTeorica,
+ dataAgendamento: parsed.dataAgendamento,
+ itens: []
+ };
+ }
+
+ dtStore[parsed.numeroTransporte].itens.push(parsed.item);
+ importadas += 1;
+ }
+
+ saveStore();
+ renderResumoImportacao();
+ statusEl.textContent = `${importadas} linha(s) importada(s) e agrupada(s) por DT.`;
+ } finally {
+ setImportLoading(false);
+ }
+}
+
+document.getElementById("importar-sap").addEventListener("click", importarLinhasSAP);
+
+function renderResumoImportacao() {
+ resumoImportacaoEl.innerHTML = "";
+ const registros = Object.values(dtStore);
+
+ if (!registros.length) {
+ resumoImportacaoEl.innerHTML = '
| Nenhuma DT importada ainda. |
';
+ return;
+ }
+
+ registros.forEach((dt) => {
+ const total = (dt.itens || []).reduce((acc, item) => acc + Number(item.qtd || 0), 0);
+ const tr = document.createElement("tr");
+ tr.innerHTML = `
+ ${dt.numeroTransporte} |
+ ${dt.nomeCliente || "-"} |
+ ${dt.itens?.length || 0} |
+ ${total} |
+ `;
+ resumoImportacaoEl.appendChild(tr);
+ });
+}
+
+function pesquisarPorFinalDT() {
+ const term = document.getElementById("busca-dt").value.trim();
+ const allDts = Object.keys(dtStore);
+ const matches = allDts.filter((dt) => !term || dt.endsWith(term));
+
+ listaDtsEl.innerHTML = "";
+ if (!matches.length) {
+ listaDtsEl.innerHTML = '';
+ return;
+ }
+
+ matches.forEach((dt) => {
+ const option = document.createElement("option");
+ option.value = dt;
+ option.textContent = `${dt} - ${dtStore[dt].nomeCliente || "Sem cliente"}`;
+ listaDtsEl.appendChild(option);
+ });
+}
+
+document.getElementById("pesquisar-dt").addEventListener("click", pesquisarPorFinalDT);
+
+function fillSheet(data) {
+ document.getElementById("sheet-dt").textContent = `DT: ${data.numeroTransporte || "-"}`;
+ document.getElementById("sheet-cliente").textContent = `Cliente: ${data.nomeCliente || "-"}`;
+ document.getElementById("sheet-data").textContent = `Data agendamento: ${data.dataAgendamento || "-"}`;
+
+ const tbody = document.getElementById("sheet-itens");
+ tbody.innerHTML = "";
+ let total = 0;
+
+ (data.itens || []).forEach((item) => {
+ total += Number(item.qtd || 0);
+ const tr = document.createElement("tr");
+ tr.innerHTML = `
+ ${data.numeroTransporte || ""} |
+ ${item.sku || ""} |
+ ${item.qtd || 0} |
+ ${item.padrao || 0} |
+ ${item.pltFechado || 0} |
+ ${item.fracao || 0} |
+ ☐ |
+ ☐ |
+ `;
+ tbody.appendChild(tr);
+ });
+
+ document.getElementById("sheet-total").textContent = total;
+}
+
+document.getElementById("carregar-dt").addEventListener("click", () => {
+ const dt = listaDtsEl.value;
+ if (!dt || !dtStore[dt]) {
+ alert("Selecione uma DT válida.");
+ return;
+ }
+ fillSheet(dtStore[dt]);
+});
+
+document.getElementById("imprimir").addEventListener("click", () => window.print());
+
+function renderStarProducts() {
+ const body = document.getElementById("produtos-estrela-body");
+ STAR_PRODUCTS.forEach(([sku, descricao]) => {
+ const tr = document.createElement("tr");
+ tr.innerHTML = `${sku} | ${descricao} | `;
+ body.appendChild(tr);
+ });
+}
+
+renderResumoImportacao();
+pesquisarPorFinalDT();
+renderStarProducts();
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..e068818
--- /dev/null
+++ b/index.html
@@ -0,0 +1,128 @@
+
+
+
+
+
+ ZLES002 | Relatório de Paletização
+
+
+
+
+
+
+
+
+
+ Importação SAP (múltiplas linhas)
+
+
+
Formato esperado por linha (TAB): Cód Cliente, Cód Transportadora, Nº transporte, Inf Agenda, Nome Cliente, Qtde Remessa, Material, Setor, Cliente Pallet, Agrupamento Regional, Qtd Teórica Convertida, Nº NFe, Quantidade Teórica, Data Agendamento.
+
+
+
+
+
+
DTs importadas no navegador
+
+
+
+ | DT |
+ Cliente |
+ Itens |
+ Total fardos |
+
+
+
+
+
+
+
+
+ Relatório para Impressão
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | Nº transporte |
+ SKU |
+ Qtde |
+ Padrão Plt |
+ Plt Fechado |
+ Fração |
+ Conferido |
+ Avaria |
+
+
+
+
+
+ | Total de fardos |
+ 0 |
+ |
+
+
+
+
+
+ Anotações do Conferente
+
+
+
+ Lacre: _____________________________________________
+
+
+
+
+ Produtos Estrela
+
+
+
+ | SKU |
+ Descrição |
+
+
+
+
+
+
+
+
+
+
+
diff --git a/styles.css b/styles.css
new file mode 100644
index 0000000..8a19f4b
--- /dev/null
+++ b/styles.css
@@ -0,0 +1,194 @@
+:root {
+ font-family: Arial, sans-serif;
+ color: #1b1b1b;
+}
+
+body {
+ margin: 0;
+ background: #f4f6f8;
+}
+
+header,
+main {
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 1rem;
+}
+
+.tabs {
+ display: flex;
+ gap: 0.5rem;
+ padding: 0 1rem 1rem;
+ max-width: 1200px;
+ margin: 0 auto;
+}
+
+.tab-btn {
+ border: 1px solid #c3c3c3;
+ background: white;
+ padding: 0.55rem 0.9rem;
+ cursor: pointer;
+ border-radius: 8px;
+}
+
+.tab-btn.active {
+ background: #003d7a;
+ color: #fff;
+ border-color: #003d7a;
+}
+
+.tab-panel {
+ display: none;
+}
+
+.tab-panel.active {
+ display: block;
+}
+
+.grid-form {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
+ gap: 0.75rem;
+ margin-bottom: 1rem;
+}
+
+label {
+ display: flex;
+ flex-direction: column;
+ font-size: 0.92rem;
+ gap: 0.25rem;
+}
+
+input,
+button,
+textarea {
+ font: inherit;
+ padding: 0.5rem;
+ border: 1px solid #c3c3c3;
+ border-radius: 8px;
+}
+
+button {
+ cursor: pointer;
+}
+
+button.primary {
+ background: #0f6d2f;
+ color: white;
+ border-color: #0f6d2f;
+}
+
+.sub-card {
+ background: white;
+ border-radius: 12px;
+ padding: 1rem;
+ margin: 1rem 0;
+ border: 1px solid #ddd;
+}
+
+.row-title {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+table {
+ width: 100%;
+ border-collapse: collapse;
+ margin-top: 0.5rem;
+ background: white;
+}
+
+th,
+td {
+ border: 1px solid #cfd4d9;
+ padding: 0.45rem;
+ text-align: left;
+ font-size: 0.9rem;
+}
+
+.print-sheet {
+ background: white;
+ margin-top: 1rem;
+ border: 1px solid #ddd;
+ padding: 1rem;
+}
+
+.sheet-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start;
+}
+
+.logo-wrap img {
+ width: 120px;
+ height: auto;
+}
+
+.notes-box {
+ border: 1px dashed #777;
+ min-height: 80px;
+}
+
+.lacre {
+ margin-top: 1.5rem;
+ font-weight: bold;
+}
+
+@media print {
+ @page {
+ size: A4 landscape;
+ margin: 12mm;
+ }
+
+ body {
+ background: white;
+ }
+
+ header,
+ .tabs,
+ #relatorio .sub-card,
+ #zles002,
+ #produtos-estrela {
+ display: none !important;
+ }
+
+ #relatorio {
+ display: block !important;
+ }
+
+ .print-sheet {
+ border: none;
+ margin: 0;
+ padding: 0;
+ }
+}
+
+.hint {
+ color: #555;
+ font-size: 0.85rem;
+}
+
+.resumo-cabecalho {
+ margin-top: 0.5rem;
+ padding: 0.5rem;
+ background: #eef5ff;
+ border: 1px solid #c7dbff;
+ border-radius: 8px;
+}
+
+
+#status.loading {
+ color: #003d7a;
+ font-weight: 600;
+}
+
+#status.loading::before {
+ content: "⏳ ";
+ animation: pulse 1s infinite;
+}
+
+@keyframes pulse {
+ 0%, 100% { opacity: 0.35; }
+ 50% { opacity: 1; }
+}