Cuidado especializado, da sua rotina ao instituto
Minha conta
Produtos no meu carrinho
Frete grátis nas compras acima de R$ 599
Parece que sua sacola
está vazia...
está vazia...
O que quer melhorar na sua pele?
Agendamento
Agende seu atendimento no instituto
1 · Escolha o atendimento
* Todos os campos são obrigatórios
Serviço
Limpeza de PeleHigienização profunda LuminnaTratamento de luminosidade
Instituto Selecione o instituto CENTER NORTEHIGIENÓPOLISITAIMJARDINSMOEMASANTANASHOPPING MORUMBI CAMBUÍ LOURDES IGUATEMI POA ASA NORTEASA SUL Data de preferência Ver horários disponíveis
2 · Escolha o horário
3 · Seus dados
Nome completo E-mail Celular (com DDD)
?
Agendamento confirmado
Fazer outro agendamento
${text}
` : ""; } function validSearch() { /* botão sempre habilitado; validação acontece no clique */ } function faltando() { const sel = ROOT.querySelector('input[name="servico"]:checked'); state.servico = sel ? sel.value : ""; if (!state.servico) return "Escolha o serviço: Limpeza de Pele ou Luminna."; if (!$("loja").value) return "Selecione o instituto."; if (!$("data").value) return "Escolha a data de preferência."; return ""; } ROOT.querySelectorAll('input[name="servico"]').forEach(r => r.addEventListener("change", () => { ROOT.querySelectorAll(".svc").forEach(l => l.classList.toggle("sel", l.querySelector("input").checked)); state.servico = r.value; resetAfterSearch(); validSearch(); })); ["loja", "data"].forEach(id => ["change", "input", "blur"].forEach(ev => $(id).addEventListener(ev, () => { if (ev === "change") resetAfterSearch(); else msg("searchMsg", ""); }))); function resetAfterSearch() { state.slot = null; $("stepSlots").classList.add("hidden"); $("stepForm").classList.add("hidden"); msg("searchMsg", ""); } /* Extrai horários de formatos variados de resposta. Retorna [{horario, data, loja}] */ function parseSlots(resp, dataPadrao, lojaPadrao) { const out = []; const timeRe = /\b([01]?\d|2[0-3])[:h]([0-5]\d)\b/g; const norm = t => { const m = String(t).match(/([01]?\d|2[0-3])[:h]([0-5]\d)/); return m ? m[1].padStart(2, "0") + ":" + m[2] : null; }; function walk(v, ctx) { if (v == null) return; if (typeof v === "string") { let m; while ((m = timeRe.exec(v))) out.push({ horario: m[1].padStart(2, "0") + ":" + m[2], ...ctx }); return; } if (Array.isArray(v)) { v.forEach(x => walk(x, ctx)); return; } if (typeof v === "object") { const c = { ...ctx }; const dKey = Object.keys(v).find(k => /^(data|date|dia)$/i.test(k)); const lKey = Object.keys(v).find(k => /^(loja|lojaPreferencia|instituto|unidade)$/i.test(k)); if (dKey && typeof v[dKey] === "string") c.data = v[dKey]; if (lKey && typeof v[lKey] === "string") c.loja = v[lKey]; const hKey = Object.keys(v).find(k => /^(horario|hora|time|inicio)$/i.test(k)); if (hKey && typeof v[hKey] === "string" && norm(v[hKey])) { out.push({ horario: norm(v[hKey]), ...c }); return; } for (const [k, val] of Object.entries(v)) { if (k === dKey || k === lKey) continue; // chave pode ser a própria data: {"28/10/2026": ["09:00", ...]} const cc = /^\d{2}\/\d{2}\/\d{4}$|^\d{4}-\d{2}-\d{2}$/.test(k) ? { ...c, data: k } : c; walk(val, cc); } } } walk(resp, { data: dataPadrao, loja: lojaPadrao }); const seen = new Set(); return out.filter(s => { const k = s.data + s.loja + s.horario; if (seen.has(k)) return false; seen.add(k); return true; }) .sort((a, b) => (a.data + a.horario).localeCompare(b.data + b.horario)); } async function postJSON(path, body) { const r = await fetch(CONFIG.API_BASE + path, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) }); const text = await r.text(); let data; try { data = JSON.parse(text); } catch (_e) { data = text; } if (!r.ok) throw new Error((data && data.mensagem) || (data && data.message) || `Erro ${r.status}`); return data; } $("btnBuscar").addEventListener("click", async () => { const falta = faltando(); if (falta) return msg("searchMsg", falta); state.loja = $("loja").value; state.data = fmtDate($("data").value); const btn = $("btnBuscar"); btn.disabled = true; btn.innerHTML = 'Buscando horários…'; resetAfterSearch(); try { const resp = await postJSON("/consultaagendanew2", { data: state.data, lojaPreferencia: state.loja, servico: state.servico }); console.log("consultaagendanew2:", resp); const slots = parseSlots(resp, state.data, state.loja); renderSlots(slots, resp); } catch (e) { msg("searchMsg", "Não foi possível consultar os horários agora. Tente novamente em instantes." + e.message + ""); } finally { btn.disabled = false; btn.textContent = "Ver horários disponíveis"; } }); function renderSlots(slots, resp) { $("stepSlots").classList.remove("hidden"); const box = $("slots"); box.innerHTML = ""; if (!slots.length) { const txt = typeof resp === "string" ? resp : (resp && (resp.mensagem || resp.message)) || ""; $("slotsInfo").innerHTML = `Não há horários disponíveis para ${state.servico} no instituto ${state.loja} em ${showDate(state.data)}. Tente outra data ou instituto.` + (txt ? `
${txt}` : ""); return; } const outrasDatas = slots.some(s => s.data !== state.data); const outrasLojas = slots.some(s => s.loja !== state.loja); $("slotsInfo").innerHTML = `${state.servico} · ${state.loja} · ${showDate(state.data)}` + (outrasDatas || outrasLojas ? "
Mostrando também as opções mais próximas disponíveis." : ""); slots.forEach(s => { const b = document.createElement("button"); b.type = "button"; b.className = "slot"; b.setAttribute("aria-pressed", "false"); const extra = [s.data !== state.data ? showDate(s.data) : "", s.loja !== state.loja ? s.loja : ""].filter(Boolean).join(" · "); b.innerHTML = s.horario + (extra ? `${extra}` : ""); b.addEventListener("click", () => { box.querySelectorAll(".slot").forEach(x => x.setAttribute("aria-pressed", "false")); b.setAttribute("aria-pressed", "true"); state.slot = s; $("finalSummary").innerHTML = `${state.servico}
${s.loja} · ${showDate(s.data)} às ${s.horario}`; $("stepForm").classList.remove("hidden"); $("stepForm").scrollIntoView({ behavior: "smooth", block: "start" }); }); box.appendChild(b); }); } // máscara de celular $("celular").addEventListener("input", e => { let d = e.target.value.replace(/\D/g, "").slice(0, 11); if (d.length > 6) e.target.value = `(${d.slice(0,2)}) ${d.slice(2, d.length - 4)}-${d.slice(-4)}`; else if (d.length > 2) e.target.value = `(${d.slice(0,2)}) ${d.slice(2)}`; else e.target.value = d; }); $("form").addEventListener("submit", async e => { e.preventDefault(); const nome = $("nome").value.trim(); const email = $("email").value.trim(); const cel = $("celular").value.replace(/\D/g, ""); if (nome.split(/\s+/).length Confirmando…'; try { const resp = await postJSON("/realizaagendamento2", { data: state.slot.data, horario: state.slot.horario, lojaPreferencia: state.slot.loja, servico: state.servico, nome, email, celular: cel, IdManychat: CONFIG.ID_MANYCHAT }); console.log("realizaagendamento2:", resp); const falhou = resp && typeof resp === "object" && (resp.sucesso === false || resp.success === false || resp.erro); if (falhou) throw new Error(resp.mensagem || resp.message || resp.erro || "Não foi possível agendar."); $("doneText").innerHTML = `${state.servico} no instituto ${state.slot.loja}
${showDate(state.slot.data)} às ${state.slot.horario}`; ["stepSearch", "stepSlots", "stepForm"].forEach(id => $(id).classList.add("hidden")); $("stepDone").classList.remove("hidden"); window.scrollTo({ top: 0, behavior: "smooth" }); } catch (err) { msg("formMsg", "Não foi possível concluir o agendamento. " + err.message); } finally { btn.disabled = false; btn.textContent = "Confirmar agendamento"; } }); $("btnNovo").addEventListener("click", () => location.reload()); })();