From 2098fb2bb2bd9fce977e3022d8af65ed88085404 Mon Sep 17 00:00:00 2001 From: tulioperdigao <116309232+tulioperdigao@users.noreply.github.com> Date: Mon, 13 Oct 2025 15:35:37 -0300 Subject: [PATCH] =?UTF-8?q?Determina=20a=20viabilidade=20para=20"Link=20De?= =?UTF-8?q?dicado"=20(<=3D=201000m)=20e=20"Link=20N=C3=A3o=20Dedicado"=20(?= =?UTF-8?q?<=3D=20500m)=20com=20base=20na=20dist=C3=A2ncia=20retornada.Est?= =?UTF-8?q?rutura=20e=20estiliza=20p=C3=A1gina=20de=20inser=C3=A7=C3=A3o?= =?UTF-8?q?=20com=20alguns=20lorem's=20enquanto=20n=C3=A3o=20h=C3=A1=20tex?= =?UTF-8?q?to=20definido.=20Insere=20l=C3=B3gica=20de=20inser=C3=A7=C3=A3o?= =?UTF-8?q?=20de=20classes=20para=20retorno=20de=20resultados=20visualment?= =?UTF-8?q?e.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/settings.json | 3 + public/assets/Wpp.png | Bin 0 -> 22437 bytes public/index.html | 91 +++++++++++---- public/main.js | 82 +++++--------- public/style.css | 135 ++++++++++++++++++++++ server.js | 256 ++---------------------------------------- 6 files changed, 241 insertions(+), 326 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 public/assets/Wpp.png diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..6f3a291 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "liveServer.settings.port": 5501 +} \ No newline at end of file diff --git a/public/assets/Wpp.png b/public/assets/Wpp.png new file mode 100644 index 0000000000000000000000000000000000000000..12109d678b932becffeb70514554a5c4b4d77bde GIT binary patch literal 22437 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 <*!0?IBflN8Qr{NTccy%0hJ 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 literal 0 HcmV?d00001 diff --git a/public/index.html b/public/index.html index 37fcf50..e1f341d 100644 --- a/public/index.html +++ b/public/index.html @@ -6,7 +6,7 @@ Validação Viabilidade - +

@@ -19,36 +19,85 @@
- -
-
-
Upload de CSV
-
-
- -
- -
-
- + +
+
+

Consulta por CEP

+
+
+

+ Lorem ipsum dolor sit, amet consectetur adipisicing elit. Harum reprehenderit modi, rerum minima vitae earum delectus repudiandae expedita, architecto voluptatum atque cumque autem repellendus. Maiores aut adipisci repellendus facilis repellat! +

-
Consulta Manual por CEP
-
-
-
+ +
+
+ + +
+
+ + +
-
+
+
+
+
+ +
+ + + +
+
+
+ +
+
+ +
+ + + +
+
+
+ +
+ +
+
+

+ Lorem ipsum dolor sit, amet consectetur adipisicing elit. Similique, odio iste. Beatae soluta a quas unde iste quia voluptatum, recusandae illo aperiam, est quibusdam? Officia deserunt temporibus at impedit repellat! +

+
+ +
+
+ Ícone Whatsapp +
+
+ + 0800 020 1337 + +
+
+
+
+
diff --git a/public/main.js b/public/main.js index 18e09b7..b258c00 100644 --- a/public/main.js +++ b/public/main.js @@ -1,69 +1,37 @@ -document.getElementById('uploadForm').addEventListener('submit', async (e) => { - e.preventDefault(); - const fileEl = document.getElementById('csvfile'); - if (!fileEl.files.length) return; - const fd = new FormData(); - fd.append('csvfile', fileEl.files[0]); - const resEl = document.getElementById('uploadResult'); - resEl.innerText = 'Enviando...'; - try { - const resp = await fetch('/upload', { method: 'POST', body: fd }); - const data = await resp.json(); - if (data.jobId) { - // show progress bar - document.getElementById('progressWrap').style.display = 'block'; - pollJob(data.jobId); - resEl.innerText = `Consultando viabilidade...`; - } else if (data.error) { - resEl.innerText = 'Erro: ' + data.error; - } - } catch (e) { - resEl.innerText = 'Erro no upload'; - } -}); - -async function pollJob(jobId) { - const resEl = document.getElementById('uploadResult'); - const bar = document.getElementById('progressBar'); - try { - const resp = await fetch(`/status/${jobId}`); - const j = await resp.json(); - if (j.total && j.total > 0) { - const pct = Math.round((j.processed / j.total) * 100); - bar.style.width = pct + '%'; - bar.innerText = `${pct}%`; - } - if (j.status === 'done') { - bar.style.width = '100%'; - bar.innerText = '100%'; - resEl.innerHTML = `Concluído. Baixar CSV processado`; - return; - } - if (j.status === 'error') { - resEl.innerText = 'Erro no processamento: ' + j.error; - return; - } - // ainda processando - setTimeout(() => pollJob(jobId), 1000); - } catch (e) { - resEl.innerText = 'Erro ao consultar status do job'; - } -} - document.getElementById('btnConsultaCep').addEventListener('click', async () => { const cep = document.getElementById('cep').value; const numero = document.getElementById('numero').value; - const el = document.getElementById('consultaResult'); - el.innerText = 'Consultando...'; + const endereco = document.getElementById('consultaResultAddress'); + const resultados = document.getElementById('consultaResultViabilidade'); + const dedicado = document.getElementById('link-dedicado'); + const naoDedicado = document.getElementById('link-nao-dedicado'); + endereco.innerText = 'Consultando...'; try { const resp = await fetch(`/consulta-cep?cep=${encodeURIComponent(cep)}&numero=${encodeURIComponent(numero)}`); const data = await resp.json(); if (data.distancia) { - el.innerText = `Endereço: ${data.endereco}\nLat: ${data.latitude} Lon: ${data.longitude}\nDistância: ${data.distancia}`; + // colocar o card-results__container (resultados) com display block + endereco.innerText = `Endereço: ${data.endereco}.`; + resultados.style.display = 'block'; + + // insere nos spans link-dedicado e link-nao-dedicado os textos de viabilidade e se for viavel adicionar classe "viavel" e se for inviavel adicionar classe "inviavel" + dedicado.innerText = data.dedicado; + naoDedicado.innerText = data.naoDedicado; + if (data.dedicado === 'Viável') { + dedicado.classList.add('viavel'); + } else if (data.dedicado === 'Não viável') { + dedicado.classList.add('inviavel'); + } + + if (data.naoDedicado === 'Viável') { + naoDedicado.classList.add('viavel'); + } else if (data.naoDedicado === 'Não viável') { + naoDedicado.classList.add('inviavel'); + } } else if (data.error) { - el.innerText = 'Erro: ' + data.error; + endereco.innerText = 'Erro: ' + data.error; } } catch (e) { - el.innerText = 'Erro na consulta'; + endereco.innerText = 'Erro na consulta'; } }); diff --git a/public/style.css b/public/style.css index 6b720fe..fa82dab 100644 --- a/public/style.css +++ b/public/style.css @@ -21,6 +21,71 @@ padding: 0px !important; } +.description__container { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + padding: 1rem; +} + +.description-title { + margin-bottom: 15px; +} + +.description-content > p { + text-align: center; +} + +.card-results__container{ + margin-top: 50px; + display: none; +} + +.card-results__container .card{ + margin-top: 5px; + min-height: 80px; + justify-content: center; +} + +.card-content { + display: flex; + align-items: center; + height: 80px; +} + +.card-content-label { + display: flex; + align-items: center; + border-right: 1px solid gray; + padding: 1rem; + width: 60%; + height: 100%; +} + +.card-content-result { + padding: 1rem; + display: flex; + justify-content: center; + width: 40%; + height: 100%; + +} + +.viavel { + font-weight: bold; + color: #234164; +} + +.inviavel { + font-weight: bold; + color: #d40000; +} + +a { + text-decoration: none; +} + button { background-color: #31a3b5 !important; border: none !important; @@ -38,6 +103,43 @@ button:hover { margin-top: 40px; } +.call__container { + margin-top: 50px; + padding: 1rem; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.call-text > p { + text-align: center; +} + +.call-button__container { + display: flex; + justify-content: center; + align-items: center; + padding: 0.5rem 1rem; + background-color: #25D366; + border-radius: 8px; + box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px; + margin-top: 15px; +} + +.call-button-phone { + margin-left: 10px; +} + +.call-button-phone > span { + color: white; + font-weight: bold; +} + +.call__container { + margin-bottom: 50px; +} + @media screen and (max-width: 768px) { .header__container { @@ -52,4 +154,37 @@ button:hover { .header-logo__container > img { width: 80px; } +} + +@media screen and (min-width: 768px) { + body > div.container > div.card > div > div.row.g-2.card-body-input__container > div:nth-child(3){ + display: flex; + align-items: flex-end ; + margin-top: 0px; + } + + .description__container { + align-items: flex-start; + padding-left: 0px; + } + + .description-content { + width: 50%; + } + + .description-content > p { + text-align: left; + } + + .call-text { + width: 50%; + } + + .card-results__container .card { + min-height: fit-content; + } + + .card-content { + height: fit-content; + } } \ No newline at end of file diff --git a/server.js b/server.js index 6eb071c..cac4d0b 100644 --- a/server.js +++ b/server.js @@ -153,252 +153,6 @@ async function getMinDistance(lat, lon) { return null; } -// upload CSV endpoint -const jobs = {}; // jobId -> { status, total, processed, download, error } - -app.post('/upload', upload.single('csvfile'), (req, res) => { - if (!req.file) return res.status(400).json({ error: 'Nenhum arquivo enviado' }); - const filePath = req.file.path; - const jobId = `${Date.now()}-${Math.random().toString(36).slice(2,8)}`; - jobs[jobId] = { status: 'queued', total: 0, processed: 0, download: null, error: null }; - - (async () => { - jobs[jobId].status = 'processing'; - try { - const rows = []; - await new Promise((resolve, reject) => { - fs.createReadStream(filePath) - .pipe(csv({ separator: ';' })) - .on('data', (data) => rows.push(data)) - .on('end', resolve) - .on('error', reject); - }); - - jobs[jobId].total = rows.length; - - const coordCache = new Map(); - - const outRows = []; - for (let i = 0; i < rows.length; i++) { - const row = rows[i]; - // normalize keys to avoid duplicates caused by different headers - const norm = {}; - Object.keys(row).forEach(k => { - // normalize header: lowercase, remove diacritics and non-alphanumeric - const kn = k.trim().toLowerCase().normalize('NFKD').replace(/[\u0300-\u036f]/g, '').replace(/[^a-z0-9]/g, ''); - norm[kn] = row[k]; - }); - - // Input columns (from normalized map) - const rawCep = norm['cep'] ? String(norm['cep']).replace(/\D/g, '') : ''; - const rawNumero = norm['numero'] ? String(norm['numero']).trim() : ''; - // prefer lat/lon from normalized input if available - const rawLat = norm['latitude'] || norm['lat'] || null; - const rawLon = norm['longitude'] || norm['lon'] || norm['long'] || null; - - // Prefer existing lat/lon if provided from normalized fields - let lat = null, lon = null; - if (rawLat && rawLon) { - lat = Number(String(rawLat).replace(',', '.')); - lon = Number(String(rawLon).replace(',', '.')); - } - - let builtAddress = ''; - - // If no coords, try ViaCEP -> Google - if (!Number.isFinite(lat) || !Number.isFinite(lon)) { - if (rawCep) { - const cep8 = rawCep.padStart(8, '0'); - const viaCepData = await fetchJson(`https://viacep.com.br/ws/${cep8}/json/`); - if (viaCepData && !viaCepData.erro) { - const logradouro = viaCepData.logradouro || ''; - const bairro = viaCepData.bairro || ''; - const cidade = viaCepData.localidade || ''; - const uf = viaCepData.uf || ''; - if (logradouro) { - builtAddress = `${logradouro}, ${rawNumero}, ${bairro}, ${cidade} - ${uf}`.replace(/, ,/g, ',').replace(/^,\s*/, ''); - } else { - // fallback: use neighborhood/city - builtAddress = `${bairro || ''} ${cidade ? ', ' + cidade : ''} ${uf ? '- ' + uf : ''}`.trim(); - } - - // build addressToUse (builtAddress already assembled above) - if (!process.env.GOOGLE_API_KEY) { - console.error('[ERROR] GOOGLE_API_KEY não definida. Não será possível geocodificar. Defina a chave no .env ou em process.env'); - } else { - const addressToUse = builtAddress || `${cidade} ${uf} ${cep8}`; - const geo = await geocodeWithGoogle(addressToUse); - if (geo) { lat = geo.lat; lon = geo.lon; } - else console.warn(`Google Geocoding não retornou resultado para '${addressToUse}' (CEP ${cep8}, row ${i+1})`); - } - } else { - console.warn(`ViaCEP erro for CEP ${rawCep} (row ${i+1})`); - } - } else { - console.log(`Row ${i+1}: missing/invalid CEP -> '${rawCep}'`); - } - } - - // Prepare explicit output row to avoid extra columns - const out = { - 'CEP': rawCep || '', - 'Número': rawNumero || '', - 'Endereço': builtAddress || '', - // write lat/lon as strings with dot decimal and fixed precision to avoid locale swaps - 'Latitude': Number.isFinite(lat) ? Number(lat).toFixed(6) : '', - 'Longitude': Number.isFinite(lon) ? Number(lon).toFixed(6) : '', - 'Não dedicado': '', - 'Dedicado': '', - 'Distancia': '', - 'Parceiro/Sothis': '' - }; - - if (Number.isFinite(lat) && Number.isFinite(lon)) { - - const coordKey = `${lat.toFixed(6)},${lon.toFixed(6)}`; - if (coordCache.has(coordKey)) { - const cached = coordCache.get(coordKey); // cached is either null or { dist, pastaSigla } - if (cached !== null) { - const d = cached.dist; - const di = Math.round(Number(d)); - out['Não dedicado'] = di <= 500 ? 'viável' : 'Não viável'; - out['Dedicado'] = di <= 1000 ? 'viável' : 'Não viável'; - out['Distancia'] = `${di}M`; - out['Parceiro/Sothis'] = normalizePartnerSigla(cached.pastaSigla) || ''; - } else { - out['Não dedicado'] = 'Não viável'; - out['Dedicado'] = 'Não viável'; - out['Distancia'] = '5km +'; - out['Parceiro/Sothis'] = ''; - } - } else { - const minResult = await getMinDistance(lat, lon); // { dist, pastaSigla } or null - coordCache.set(coordKey, minResult); - if (minResult !== null) { - const di = Math.round(Number(minResult.dist)); - out['Não dedicado'] = di <= 500 ? 'viável' : 'Não viável'; - out['Dedicado'] = di <= 1000 ? 'viável' : 'Não viável'; - out['Distancia'] = `${di}M`; - out['Parceiro/Sothis'] = normalizePartnerSigla(minResult.pastaSigla) || ''; - } else { - out['Não dedicado'] = 'Não viável'; - out['Dedicado'] = 'Não viável'; - out['Distancia'] = '5km +'; - out['Parceiro/Sothis'] = ''; - } - await sleep(REQUEST_DELAY_MS); - } - } else { - // no coords available -> keep defaults - } - - outRows.push(out); - jobs[jobId].processed = i + 1; - } - - // write output csv - use explicit outRows and fixed header order - const outPath = path.join(__dirname, 'outputs'); - if (!fs.existsSync(outPath)) fs.mkdirSync(outPath); - const originalName = (req.file && req.file.originalname) ? req.file.originalname : `upload_${Date.now()}.csv`; - const parsed = path.parse(originalName); - let outBase = `${parsed.name}_output`; - let outFile = path.join(outPath, `${outBase}.csv`); - if (fs.existsSync(outFile)) { - outFile = path.join(outPath, `${outBase}_${Date.now()}.csv`); - } - const headers = ['CEP','Número','Endereço','Latitude','Longitude','Não dedicado','Dedicado','Distancia','Parceiro/Sothis']; - await new Promise((resolve, reject) => { - const ws = fs.createWriteStream(outFile); - ws.write('\uFEFF'); - fastCsv.write(outRows, { headers: headers, delimiter: ';' }).pipe(ws).on('finish', resolve).on('error', reject); - }); - - try { fs.unlinkSync(filePath); } catch (e) {} - - jobs[jobId].status = 'done'; - jobs[jobId].download = `/download/${path.basename(outFile)}`; - } catch (err) { - console.error(err); - jobs[jobId].status = 'error'; - jobs[jobId].error = String(err.message || err); - } - })(); - - return res.json({ jobId }); -}); - -// download endpoint -app.get('/download/:name', (req, res) => { - const name = req.params.name; - const p = path.join(__dirname, 'outputs', name); - if (!fs.existsSync(p)) return res.status(404).send('Arquivo não encontrado'); - res.download(p); -}); - -// job status endpoint -app.get('/status/:jobId', (req, res) => { - const job = jobs[req.params.jobId]; - if (!job) return res.status(404).json({ error: 'job não encontrado' }); - return res.json(job); -}); - -// manual query endpoint -// /consulta now accepts either latitude+longitude OR cep+numero. If cep is provided we resolve ViaCEP -> Google -> Geogrid -app.get('/consulta', async (req, res) => { - const { latitude: rawLat, longitude: rawLon, cep: rawCep, numero: rawNumero } = req.query; - - // If cep provided, use ViaCEP -> Google geocoding -> Geogrid - if (rawCep) { - const cep = String(rawCep).replace(/\D/g, ''); - const numero = rawNumero ? String(rawNumero).trim() : ''; - try { - const viaCepData = await fetchJson(`https://viacep.com.br/ws/${cep}/json/`); - if (!viaCepData || viaCepData.erro) return res.status(404).json({ error: 'CEP não encontrado' }); - const logradouro = viaCepData.logradouro || ''; - const bairro = viaCepData.bairro || ''; - const cidade = viaCepData.localidade || ''; - const uf = viaCepData.uf || ''; - const endereco = `${logradouro}, ${numero}, ${bairro}, ${cidade} - ${uf}`.replace(/, ,/g, ',').replace(/^,\s*/, ''); - - if (!process.env.GOOGLE_API_KEY) return res.status(500).json({ error: 'GOOGLE_API_KEY não definida no servidor' }); - const geo = await geocodeWithGoogle(endereco || `${cidade} ${uf} ${cep}`); - if (!geo) return res.status(404).json({ error: 'geocode não encontrado (Google)' }); - const lat = Number(geo.lat); - const lon = Number(geo.lon); - const result = await getMinDistance(lat, lon); - if (result && result.dist !== undefined) { - return res.json({ endereco, latitude: lat, longitude: lon, distancia: result.dist, parceiro: result.pastaSigla || '' }); - } - return res.json({ endereco, latitude: lat, longitude: lon, distancia: '5km +' }); - } catch (err) { - console.error(err); - return res.status(500).json({ error: 'Erro na consulta' }); - } - } - - // Otherwise require latitude+longitude - if (!rawLat || !rawLon) return res.status(400).json({ error: 'latitude e longitude são obrigatórios (ou forneça cep)' }); - const latitude = Number(String(rawLat).replace(',', '.')); - const longitude = Number(String(rawLon).replace(',', '.')); - if (!Number.isFinite(latitude) || !Number.isFinite(longitude)) { - console.warn(`Consulta manual com parâmetros inválidos: lat='${rawLat}' lon='${rawLon}'`); - return res.status(400).json({ error: 'latitude ou longitude inválidos' }); - } - - try { - console.log(`Consulta manual: lat=${latitude} lon=${longitude}`); - const result = await getMinDistance(latitude, longitude); - console.log(`Resultado consulta manual: ${JSON.stringify(result)}`); - if (result && result.dist !== undefined) { - return res.json({ distancia: result.dist, parceiro: result.pastaSigla || '' }); - } - return res.json({ distancia: '5km +' }); - } catch (err) { - console.error(err); - return res.status(500).json({ error: 'Erro na consulta' }); - } -}); - // manual CEP+Numero query: resolves ViaCEP -> Nominatim -> Geogrid app.get('/consulta-cep', async (req, res) => { const { cep: rawCep, numero: rawNumero } = req.query; @@ -422,9 +176,15 @@ app.get('/consulta-cep', async (req, res) => { const lon = Number(geo.lon); const result = await getMinDistance(lat, lon); if (result && result.dist !== undefined) { - return res.json({ endereco, latitude: lat, longitude: lon, distancia: result.dist, parceiro: result.pastaSigla || '' }); + // preciso criar 2 campos: Link Dedicado e Link Não Dedicado em que o dedicado é viável até 1000m e o não dedicado até 500m + if (result.dist <= 500) { + return res.json({ endereco, latitude: lat, longitude: lon, distancia: result.dist, dedicado: 'Viável', naoDedicado: 'Viável' }); + } else if (result.dist <= 1000) { + return res.json({ endereco, latitude: lat, longitude: lon, distancia: result.dist, dedicado: 'Viável', naoDedicado: 'Não viável' }); + } else { + return res.json({ endereco, latitude: lat, longitude: lon, distancia: result.dist, dedicado: 'Não viável', naoDedicado: 'Não viável' }); + } } - return res.json({ endereco, latitude: lat, longitude: lon, distancia: '5km +' }); } catch (err) { console.error(err); return res.status(500).json({ error: 'erro na consulta' });