/* global React, ReactDOM, qrcode */ // Agendamento no ar: mesma página aprovada, com links, horários, reservas e Pix vindos de api.php. const DS = window.AdrianoBarbozaDesignSystem_6f93c0; const AG = window.AG; const { NOW, T, PAISES, BY_CODE } = AG; AG.previa = /[?&]previa=1/.test(location.search); // Prévia dentro do painel: o editor manda a configuração por mensagem e nada é gravado. function agPrevHorarios(l, de, ate) { const out = { dias: {}, cheios: [] }, min = Date.now() + (+l.antecedencia || 0) * 36e5, max = Date.now() + (+l.janela || 90) * 864e5, dur = +l.dur || 60, passo = +l.passo || 30; for (let d = new Date(de + "T00:00:00"); AG.ymd(d) <= ate; d.setDate(d.getDate() + 1)) { const livres = []; ((l.horarios || {})[String(d.getDay())] || []).forEach(([a, b]) => { const am = +a.slice(0, 2) * 60 + +a.slice(3, 5), bm = +b.slice(0, 2) * 60 + +b.slice(3, 5); for (let t = am; t + dur <= bm; t += passo) { const ts = new Date(d.getFullYear(), d.getMonth(), d.getDate(), Math.floor(t / 60), t % 60); if (ts >= min && ts <= max) livres.push(AG.pad(Math.floor(t / 60)) + ":" + AG.pad(t % 60)); } }); if (livres.length) out.dias[AG.ymd(d)] = livres; } return out; } async function agMock(acao, params, body) { const l = AG.prevLink || {}; await new Promise((ok) => setTimeout(ok, 150)); if (acao === "horarios") return agPrevHorarios(l, params.de, params.ate); if (acao === "reservar") { const total = (+l.preco || 0) * Math.max(1, (body.horarios || []).length), pix = total > 0 && l.exigirPix !== false; return { ok: true, status: pix ? "aguardando_pix" : "confirmado", token: "previa", credito: 0, pix: pix ? { codigo: "00020126580014br.gov.bcb.pix0136PREVIA-NADA-E-COBRADO5204000053039865406" + total.toFixed(2) + "5802BR6304PREV", valor: total, txid: "PREVIA", expira: Math.floor(Date.now() / 1000) + 7200 } : null }; } if (acao === "verificar_form") return { feito: false }; return { ok: true }; } const agApiReal = async (acao, params, body) => { const r = await fetch("api.php?acao=" + acao + (params ? "&" + new URLSearchParams(params) : ""), body ? { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) } : undefined); const j = await r.json().catch(() => ({})); if (!r.ok) throw Object.assign(new Error(j.erro || "Erro " + r.status), { code: r.status }); return j; }; const agApi = (acao, params, body) => AG.previa ? agMock(acao, params, body) : agApiReal(acao, params, body); const agQr = (s) => { try { const q = qrcode(0, "M"); q.addData(s); q.make(); return q.createSvgTag({ cellSize: 4, margin: 2, scalable: true }); } catch (e) { return ""; } }; // Field overrides on top of DS.Input: 10px radius, 16px text (no iOS zoom), // 48px min height for touch, and a slightly stronger hairline on off-white. const agInput = { borderRadius: 10, fontSize: 16, minHeight: 48, borderColor: "rgba(0,0,0,0.28)" }; function AgCTA({ children, disabled = false, style = {}, ...rest }) { return ( { if (!disabled) e.currentTarget.style.background = "var(--yellow)"; }} onMouseLeave={(e) => { e.currentTarget.style.background = "var(--salmon)"; e.currentTarget.style.transform = "scale(1)"; }} {...rest}>{children} ); } function AgGhost({ children, style = {}, ...rest }) { return {children}; } function IconLock() { return (); } function IconClock() { return (); } const IconCheck = ({ s = 14 }) => ( ); // Thin progress line between step numbers; the current number fills black. const STEP_ORDER = { escolher: 0, dados: 1, pagar: 2, expirou: 2, feito: 3 }; function Steps({ L, step, pago }) { const labels = pago ? L.etapas4 : L.etapas3; const idx = pago ? STEP_ORDER[step] : ({ escolher: 0, dados: 1, expirou: 1, pagar: 1, feito: 2 })[step]; const n = labels.length; return (
    {labels.map((l, i) => (
  1. {i < idx ? : i + 1} {l}
  2. ))}
); } // Height-animated disclosure; content is inert while closed. function Collapse({ open, children }) { return (
{children}
); } function OptBtn({ open, onClick, children }) { return ( ); } function FlagBR() { return (); } function FlagUK() { return (); } function LangSwitch({ lang, setLang }) { return (
); } // Slot time in the visitor's zone, with Brasília in parentheses when they differ. function useZone(lang, fuso) { const tz = fuso === "lisboa" ? "Europe/Lisbon" : (() => { try { return Intl.DateTimeFormat().resolvedOptions().timeZone || "America/Sao_Paulo"; } catch (e) { return "America/Sao_Paulo"; } })(); const isBRT = AG.sameAsBRT(tz); const locale = lang === "en" ? "en-GB" : "pt-BR"; const label = (day, s) => { if (isBRT) return { main: AG.fmtHora(s), brt: null, shift: 0 }; const z = AG.inZone(day, s, tz, locale); return { main: z.hm, brt: AG.fmtHora(s), shift: z.shift }; }; return { tz, isBRT, label, tzName: tz.replace(/_/g, " ") }; } function Calendar({ L, month, setMonth, day, setDay, dur }) { const first = new Date(month.getFullYear(), month.getMonth(), 1); const days = new Date(month.getFullYear(), month.getMonth() + 1, 0).getDate(); const cells = []; for (let i = 0; i < first.getDay(); i++) cells.push(null); for (let i = 1; i <= days; i++) cells.push(new Date(month.getFullYear(), month.getMonth(), i)); const atStart = month.getFullYear() === NOW.getFullYear() && month.getMonth() === NOW.getMonth(); const lim = AG.limite || new Date(NOW.getFullYear(), NOW.getMonth() + 3, 0); const atEnd = month.getFullYear() * 12 + month.getMonth() >= lim.getFullYear() * 12 + lim.getMonth(); const fdia = (d) => L.dia(d, L.meses, L.dowL); return (

{L.meses[month.getMonth()]} {month.getFullYear()}

{L.dow.map((d) =>
{d}
)} {cells.map((d, i) => { if (!d) return ; const n = AG.slotsFor(d, dur).length; const full = AG.isFull(d, dur); const on = AG.sameDay(d, day); return ( ); })}
); } function PhoneField({ L, lang, value, setValue, country, setCountry, error }) { const isBR = country === "BR"; const onType = (e) => { const raw = e.target.value; if (/^\s*\+/.test(raw)) { const digits = raw.replace(/\D/g, ""); const hit = BY_CODE.find((p) => digits.startsWith(p[3])); if (hit) { setCountry(hit[0]); const rest = digits.slice(hit[3].length); setValue(hit[0] === "BR" ? AG.maskBR(rest) : AG.maskIntl(rest)); return; } setValue("+" + digits); return; } const d = raw.replace(/\D/g, ""); setValue(isBR ? AG.maskBR(d) : AG.maskIntl(d)); }; return (
); } const URL_OK = (u) => /^https:\/\/\S+\.\S+/.test(u.trim()); function MatInputs({ L, url, desc, setUrl, setDesc, bad, idp }) { const svc = url.trim() ? AG.svcName(url) : null; return (
setUrl(e.target.value)} placeholder="https://" aria-label={L.mat} invalid={bad} aria-invalid={bad} style={agInput} /> setDesc(e.target.value)} placeholder={L.matDesc} aria-label={L.matDesc} style={agInput} /> {bad ? {L.e.url} : svc ? {svc} : null}
); } // Typed links become numbered blocks when added; each block can be edited or removed. function MaterialsField({ L, items, setItems, draft, setDraft, tried }) { const [edit, setEdit] = React.useState(null); // { i, url, desc } const [draftBad, setDraftBad] = React.useState(false); const add = () => { if (!URL_OK(draft.url)) { setDraftBad(true); return; } setItems([...items, { url: draft.url.trim(), desc: draft.desc.trim() }]); setDraft({ url: "", desc: "" }); setDraftBad(false); }; const editBad = edit && !URL_OK(edit.url); return (
{L.mat} · {L.matOpt}

{L.matHint}

{items.length ? (
    {items.map((it, i) => (
  1. {L.linkN(i + 1)} {AG.svcName(it.url) ? {AG.svcName(it.url)} : null}
    {edit && edit.i === i ? ( setEdit({ ...edit, url: v })} setDesc={(v) => setEdit({ ...edit, desc: v })} />
    ) : ( {it.desc ?

    {it.desc}

    : null} {it.url}
    )}
  2. ))}
) : null} {items.length < 5 ? (
{ setDraft({ ...draft, url: v }); setDraftBad(false); }} setDesc={(v) => setDraft({ ...draft, desc: v })} /> {tried && draft.url.trim() && !draftBad ? {L.linkPend} : null}
) : {L.matMax}}
); } function GuestsField({ L, guests, setGuests }) { const [v, setV] = React.useState(""); const [err, setErr] = React.useState(""); const add = () => { const e = v.trim(); if (!e) return; if (!/^\S+@\S+\.\S+$/.test(e)) { setErr(L.convErr); return; } if (guests.indexOf(e) === -1 && guests.length < 5) setGuests([...guests, e]); setV(""); setErr(""); }; return (
{L.conv} · {L.matOpt}

{L.convHint}

{guests.length ? ( ) : null} {guests.length < 5 ? (
{ setV(e.target.value); setErr(""); }} onKeyDown={(e) => { if (e.key === "Enter") { e.preventDefault(); add(); } }} placeholder="nome@exemplo.com" aria-label={L.conv} invalid={!!err} style={agInput} /> {L.convAdd}
) : null} {err ? {err} : null}
); } function RecurrenceField({ L, cfg, rep, setRep, day, slot, zone, tried }) { const freqs = cfg.recorrencia.freqs; const list = rep.on ? AG.series(day, slot, rep.freq, rep.qtd, cfg.dur) : []; const decide = (i, v) => setRep({ ...rep, dec: { ...rep.dec, [i]: v } }); const reset = (patch) => setRep({ ...rep, ...patch, dec: {} }); const pend = list.some((it, i) => it.status === "ajustado" && !rep.dec[i]); return (
{L.rep} {rep.on ? (
{freqs.map((fq) => ( ))}
    {list.map((it, i) => { const z = it.s ? zone.label(it.d, it.s) : null; const dec = rep.dec[i]; let right; if (it.status === "ok") right = {z.main} · {L.repOk}; else if (it.status === "sem" || dec === "nao") right = {L.repSem}; else if (dec === "sim") right = {z.main} · {L.repOk}; else right = ( {L.sugestao(z.main)} ); return (
  1. {L.diaCurto(it.d, L.meses)}{right}
  2. ); })}
{tried && pend ? {L.repPend} : null}
) : null}
); } // Collapsed by default: a count + progress, and a toggle to show each item. function PendingPanel({ L, lang, st, setSt, modo }) { const p = AG.PESSOAL; const total = p.formularios.length + p.tarefas.length; const feitos = p.formularios.filter((f) => st.forms[f.id]).length + p.tarefas.filter((_, i) => st.tasks[i]).length; const pend = total - feitos; const formsOpen = p.formularios.some((f) => !st.forms[f.id]); const upd = (k, v) => setSt({ ...st, [k]: { ...st[k], ...v } }); // Preview: the first check finds nothing (form not sent yet); the second // finds the response. In production this reads the Forms response sheet. const check = async (id) => { try { const j = await agApi("verificar_form", null, { slug: AG.slug, p: AG.p, id }); if (j.feito) upd("forms", { [id]: true }); else upd("miss", { [id]: true }); } catch (e) { upd("miss", { [id]: true }); } }; return (
{L.ola(p.nome)} · {p.projeto[lang]} {pend ? L.pendDetect(pend) : L.nenhumaPend} {L.progresso(feitos, total)}
{st.aberto ? (
{L.forms}
{L.tarefas}
) : null} {formsOpen ?

{modo === "exige" ? L.bloqueado : L.lembretePend}

: null}
); } function Payment({ L, preco, pix, onPaid, onExpire }) { const [until] = React.useState(() => pix.expira * 1000); const [now, setNow] = React.useState(Date.now()); const [copied, setCopied] = React.useState(false); React.useEffect(() => { const id = setInterval(() => setNow(Date.now()), 1000); return () => clearInterval(id); }, []); const left = Math.max(0, until - now); React.useEffect(() => { if (left === 0) onExpire(); }, [left === 0]); const hh = Math.floor(left / 3600e3), mm = Math.floor((left % 3600e3) / 60e3), ss = Math.floor((left % 60e3) / 1e3); const code = pix.codigo; const copy = () => { try { navigator.clipboard.writeText(code); } catch (e) {} setCopied(true); setTimeout(() => setCopied(false), 2000); }; const pct = left / (2 * 3600e3); return (

{L.pagTit}

{L.restam} {hh}:{AG.pad(mm)}:{AG.pad(ss)}

{L.pagTxt}

{L.valorPag}R$ {preco.toFixed(2).replace(".", ",")}{L.idPix}: {pix.txid}
{L.copia} {code} {copied ? L.copiado : L.copiar}
{L.jaPaguei}
); } function App({ t, cfgL, oferta }) { const [lang, setLang] = React.useState(() => { try { const q = new URLSearchParams(location.search).get("lang"); if (q === "en" || q === "pt") return q; } catch (e) {} if (AG.previa) return "pt"; return (navigator.language || "pt").toLowerCase().startsWith("pt") ? "pt" : "en"; }); const L = T[lang]; const cfg = cfgL; const zone = useZone(lang, t.fuso); const titulo = (t.titulo || "").trim() || cfg.titulo[lang]; const subtitulo = (t.subtitulo || "").trim() || cfg.sub[lang]; const presencial = cfg.sala === "presencial"; const preco = t.pago && cfg.preco ? cfg.preco : 0; const fdia = (d) => L.dia(d, L.meses, L.dowL); React.useEffect(() => { document.documentElement.lang = lang === "en" ? "en" : "pt-BR"; }, [lang]); const [tick, setTick] = React.useState(0); const [res, setRes] = React.useState(null); const [busy, setBusy] = React.useState(false); const [erroEnv, setErroEnv] = React.useState(""); const firstAvail = React.useMemo(() => { for (let i = 0; i < 60; i++) { const d = new Date(NOW.getFullYear(), NOW.getMonth(), NOW.getDate() + i); if (AG.slotsFor(d, cfg.dur).length) return d; } return null; }, [cfg.dur, tick]); const [month, setMonth] = React.useState(new Date(NOW.getFullYear(), NOW.getMonth(), 1)); const [day, setDay] = React.useState(firstAvail); const [slot, setSlot] = React.useState(null); const [step, setStep] = React.useState("escolher"); React.useEffect(() => { AG.onUpdate = () => setTick((x) => x + 1); }, []); React.useEffect(() => { const a = new Date(month.getFullYear(), month.getMonth(), 1), b = new Date(month.getFullYear(), month.getMonth() + 1, 0), k = AG.ymd(a); if (AG.carregados[k]) return; AG.carregados[k] = 1; agApi("horarios", { l: AG.slug, de: AG.ymd(a), ate: AG.ymd(b), ...(AG.o ? { o: AG.o } : {}) }) .then((j) => { Object.assign(AG.AVAIL.dias, j.dias || {}); AG.AVAIL.cheios = AG.AVAIL.cheios.concat(j.cheios || []); setTick((x) => x + 1); }) .catch(() => { AG.carregados[k] = 0; }); }, [month, tick === -1]); const [f, setF] = React.useState({ nome: (oferta && oferta.nome) || (AG.PESSOAL && AG.PESSOAL.nome) || "", email: (oferta && oferta.email) || (cfgL.pessoal && cfgL.pessoal.email) || "", tel: "", assunto: "", endereco: "", cliente: (cfgL.clientes || []).length === 1 ? cfgL.clientes[0] : "", wpp: true, lgpd: false }); const [pais, setPais] = React.useState("BR"); const [mats, setMats] = React.useState([]); const [matDraft, setMatDraft] = React.useState({ url: "", desc: "" }); const [guests, setGuests] = React.useState([]); const [rep, setRep] = React.useState({ on: false, freq: "semanal", qtd: 4, dec: {} }); const [pendSt, setPendSt] = React.useState({ forms: AG.PESSOAL ? Object.fromEntries(AG.PESSOAL.formularios.filter((x) => x.feito).map((x) => [x.id, true])) : {}, tasks: {}, miss: {}, aberto: false }); const formsDone = pendSt.forms; const [espera, setEspera] = React.useState(null); const [paid, setPaid] = React.useState(false); const [tried, setTried] = React.useState(false); const [opt, setOpt] = React.useState({ rep: false, mat: false, conv: false }); const [maisInfo, setMaisInfo] = React.useState(false); const prevStep = React.useRef(step); const dir = STEP_ORDER[step] >= STEP_ORDER[prevStep.current] ? "fwd" : "back"; React.useEffect(() => { prevStep.current = step; }, [step]); React.useEffect(() => { setDay(firstAvail); setSlot(null); setStep("escolher"); setEspera(null); setPaid(false); setOpt({ rep: false, mat: false, conv: false }); setRep((r) => ({ ...r, on: false, freq: cfg.recorrencia ? cfg.recorrencia.freqs[0] : "semanal", qtd: 4, dec: {} })); }, [t.link]); React.useEffect(() => { if (!day && firstAvail && !oferta) setDay(firstAvail); }, [firstAvail]); React.useEffect(() => { if (oferta) { const d = new Date(oferta.dia + "T00:00:00"); setMonth(new Date(d.getFullYear(), d.getMonth(), 1)); setDay(d); setSlot({ h: +oferta.hora.slice(0, 2), m: +oferta.hora.slice(3, 5) }); } }, []); const pendOpen = t.pessoal && AG.PESSOAL.formularios.some((x) => !formsDone[x.id]); const locked = t.pessoal && t.pendModo === "exige" && pendOpen; const slots = day ? AG.slotsFor(day, cfg.dur) : []; const dayFull = day && AG.isFull(day, cfg.dur); const set = (k) => (e) => setF((o) => ({ ...o, [k]: e.target.type === "checkbox" ? e.target.checked : e.target.value })); const needAddr = presencial && t.localModo === "pessoa"; const telDigits = f.tel.replace(/\D/g, ""); const matsBad = matDraft.url.trim() !== ""; // typed but not added yet const errs = { nome: !f.nome.trim() ? L.e.nome : "", email: !/^\S+@\S+\.\S+$/.test(f.email) ? L.e.email : "", tel: (pais === "BR" ? telDigits.length < 10 : telDigits.length < 6) ? L.e.tel : "", assunto: !f.assunto.trim() ? L.e.assunto : "", cliente: (cfg.clientes || []).length && !f.cliente ? L.e.cliente : "", endereco: needAddr && !f.endereco.trim() ? L.e.end : "", lgpd: !f.lgpd ? L.e.lgpd : "", }; const ok = Object.values(errs).every((x) => !x) && !(cfg.materiais && opt.mat && matsBad) && !(rep.on && cfg.recorrencia && slot && AG.series(day, slot, rep.freq, rep.qtd, cfg.dur).some((x, i) => x.status === "ajustado" && !rep.dec[i])); const calLinks = () => { const st = new Date(Date.UTC(day.getFullYear(), day.getMonth(), day.getDate(), slot.h + 3, slot.m)); const en = new Date(st.getTime() + cfg.dur * 60000); const z = (x) => x.toISOString().replace(/[-:]/g, "").replace(/\.\d{3}/, ""); const text = titulo + " · Adriano Barboza"; const g = "https://calendar.google.com/calendar/render?action=TEMPLATE&text=" + encodeURIComponent(text) + "&dates=" + z(st) + "/" + z(en) + "&location=" + encodeURIComponent(local); const o = "https://outlook.live.com/calendar/0/deeplink/compose?subject=" + encodeURIComponent(text) + "&startdt=" + st.toISOString() + "&enddt=" + en.toISOString() + "&location=" + encodeURIComponent(local); const ics = ["BEGIN:VCALENDAR", "VERSION:2.0", "PRODID:-//Adriano Barboza//Agendamento//PT", "BEGIN:VEVENT", "UID:" + z(st) + "@adrianobarboza.com", "DTSTAMP:" + z(new Date()), "DTSTART:" + z(st), "DTEND:" + z(en), "SUMMARY:" + text, "LOCATION:" + local, "END:VEVENT", "END:VCALENDAR"].join("\r\n"); return { g, o, a: "data:text/calendar;charset=utf-8," + encodeURIComponent(ics) }; }; const salaTxt = presencial ? (t.localModo === "pessoa" ? L.presPessoa : L.presFixo + " · " + cfg.endereco) : L.sala[cfg.sala]; const local = presencial ? (t.localModo === "pessoa" ? (f.endereco || L.presPessoa) : cfg.endereco) : L.sala[cfg.sala]; const mapsUrl = "https://www.google.com/maps/search/?api=1&query=" + encodeURIComponent(f.endereco || ""); const nMats = mats.length; const hora = (d, s) => { const z = zone.label(d, s); return z.main + (z.shift ? " (" + (z.shift > 0 ? L.diaSeg : L.diaAnt) + ")" : "") + (z.brt ? " · " + z.brt + " " + L.brasilia : ""); }; const repList = rep.on && cfg.recorrencia && slot ? AG.series(day, slot, rep.freq, rep.qtd, cfg.dur) : []; const repN = repList.filter((x, i) => x.status === "ok" || (x.status === "ajustado" && rep.dec[i] === "sim")).length; const repPend = repList.some((x, i) => x.status === "ajustado" && !rep.dec[i]); const enviar = async () => { setBusy(true); setErroEnv(""); const lista = rep.on && cfg.recorrencia ? repList.map((x, i) => x.s && (x.status === "ok" || (x.status === "ajustado" && rep.dec[i] === "sim")) ? { dia: AG.ymd(x.d), hora: AG.fmtHora(x.s) } : null).filter(Boolean) : [{ dia: AG.ymd(day), hora: AG.fmtHora(slot) }]; try { const tel = f.tel ? "+" + (PAISES.find((p) => p[0] === pais) || ["", "", "", "55"])[3] + " " + f.tel : ""; const j = await agApi("reservar", null, { slug: AG.slug, p: AG.p || "", oferta: AG.o || "", nome: f.nome, email: f.email, tel, wpp: f.wpp, cliente: f.cliente, pauta: f.assunto, endereco: f.endereco, lgpd: f.lgpd, lang, materiais: opt.mat ? mats : [], convidados: opt.conv ? guests : [], horarios: lista }); setRes(j); setStep(j.status === "aguardando_pix" ? "pagar" : "feito"); } catch (e) { setErroEnv(e.message); if (e.code === 409) { AG.carregados = {}; AG.AVAIL.dias = {}; AG.AVAIL.cheios = []; setMonth(new Date(month.getTime())); } } setBusy(false); }; const left = (
{L.eyebrow} · {cfg.dur} {L.min}

{titulo}{subtitulo ? {subtitulo} : null}

{cfg.dur} {L.min} · {salaTxt}{preco ? " · " + L.valorV(preco) : ""}

{cfg.texto[lang]}

Adriano Barboza · {L.sign}
); let right; if (step === "escolher") { let body; if (locked) body =
{L.bloqueado}
; else if (dayFull) { body = espera && AG.sameDay(espera, day) ? (

{L.espOk(fdia(day))}

) : (

{L.espTit}

{L.espTxt}

{L.espDados}

{[["nome", "text", "name"], ["email", "email", "email"]].map(([k, ty, ac]) => )}
{erroEnv ? {erroEnv} : null}
{ if (!f.nome.trim()) { setErroEnv(L.e.nome); return; } if (!/^\S+@\S+\.\S+$/.test(f.email)) { setErroEnv(L.e.email); return; } setBusy(true); setErroEnv(""); try { await agApi("espera", null, { slug: AG.slug, dia: AG.ymd(day), nome: f.nome, email: f.email, lang }); setEspera(day); } catch (e) { setErroEnv(e.message); } setBusy(false); }}>{L.espEntrar}
); } else if (day && slots.length) { body = (
{slots.map((s, i) => { const on = slot && slot.h === s.h && slot.m === s.m; const z = zone.label(day, s); return ( ); })}
); } else body =
{L.vazio}
; right = (
{ setDay(d); setSlot(null); }} dur={cfg.dur} />
{zone.isBRT ? L.tzBRT : L.tzVoce(zone.tzName)}

{L.janelaTxt(cfg.janela)}

{day ? fdia(day) : L.escolhaDia}
{locked ? : null} {body} {!locked && !dayFull ? (
setStep("dados")}> {slot ? L.continuar(zone.label(day, slot).main) : L.escolhaHor}
) : null}
); } else if (step === "dados") { right = (
{ e.preventDefault(); setTried(true); if (ok && !busy) enviar(); }}>
{fdia(day)}, {hora(day, slot)}
{t.pessoal ? : null} {[["nome", "text", "name"], ["email", "email", "email"]].map(([k, ty, ac]) => ( ))} setF((o) => ({ ...o, tel: v }))} country={pais} setCountry={setPais} error={tried ? errs.tel || undefined : undefined} /> {(cfg.clientes || []).length ? (
{cfg.clientes.map((c) => )}
) : null}