// mb-storekinds.jsx — Agenda (booking) & Vitrine (showcase) stores + sheets function StoreHero({ p, onBack, badge, isFav, onToggleFav, onReview }) { return ( <>
{p.coverUrl ? {p.name} : }
{ const url = window.location.href.split('?')[0] + '?loja=' + (p._raw?.slug || p.id); if (navigator.share) { navigator.share({ title: p.name, text: p.blurb || 'Confira no Morar Bem!', url }); } else { navigator.clipboard?.writeText(url).then(() => {}); } }} />
{p.logoUrl ? {p.name} : }

{p.name}

{badge &&
{badge}
}
{p.cat} · {p.area}
{p.reviews > 0 && ( {parseFloat(p.rating).toFixed(1)} ({p.reviews}) )} {p.area} {onReview && ( )}
); } // ── Barra de contato rápido — canais de comunicação (WhatsApp, Ligar, Instagram) ── function ContactBar({ p }) { const hasAny = p.whatsapp || p.phone || p.instagram; if (!hasAny) return null; const tileLink = (icon, label, href) => (
{icon}
{label}
); return (
{p.whatsapp && tileLink( , "WhatsApp", "https://wa.me/55" + p.whatsapp.replace(/\D/g, "") )} {p.phone && !p.whatsapp && tileLink( , "Ligar", "tel:" + p.phone.replace(/\D/g, "") )} {p.instagram && tileLink( , "Instagram", "https://instagram.com/" + p.instagram.replace(/^@/, "") )}
); } // ── Agenda store ───────────────────────────────────────────── function AgendaStore({ partner, onBack, onBook, onNeedLogin }) { const companyId = partner._raw?.id || partner.id; const [detail, setDetail] = React.useState(null); React.useEffect(() => { if (!companyId) return; API.company(companyId).then(c => setDetail(apiCompanyToPartner(c))).catch(() => {}); }, [companyId]); const p = detail || partner; const totalCb = p.cashback + p.turbo; const [isFav, setIsFav] = React.useState(false); React.useEffect(() => { if (!companyId || !Auth.isLogged()) return; API.me.favorites().then(d => { setIsFav((d.favorites || []).some(f => f.id === companyId)); }).catch(() => {}); }, [companyId]); const toggleFav = () => { if (!Auth.isLogged()) { onNeedLogin?.(); return; } if (isFav) { API.me.removeFavorite(companyId).catch(() => {}); setIsFav(false); } else { API.me.addFavorite(companyId).catch(() => {}); setIsFav(true); } }; const [apiServices, setApiServices] = React.useState(null); const [videoOpen, setVideoOpen] = React.useState(false); const [contactOpen, setContactOpen] = React.useState(false); React.useEffect(() => { API.services(companyId).then(d => { const svcs = (d.services || []).filter(s => s.active).map(s => ({ id: s.id, name: s.name, desc: s.description || "", price: s.price ? parseFloat(s.price) : null, dur: s.duration_min ? Math.floor(s.duration_min / 60) + "h" + (s.duration_min % 60 ? (s.duration_min % 60) + "min" : "") : "—", _raw: s, })); if (svcs.length) setApiServices(svcs); }).catch(() => {}); }, [companyId]); const services = apiServices || servicesFor(p); const [reviewOpen, setReviewOpen] = React.useState(false); const handleReview = () => { if (!Auth.isLogged()) { onNeedLogin?.(); return; } setReviewOpen(true); }; return (
Agendamento {totalCb > 0 && } {p.turbo > 0 && } } /> {/* botão de vídeo */} {p.video && (
)} {/* Botões: Localização + Contato */} {(p.address || p.whatsapp || p.phone) && (
{p.address && ( Localização )} {(p.whatsapp || p.phone) && ( )}
)} {p.horario && (
{p.horario}
)}
Escolha o serviço, a data e o horário. Você confirma sem pagar agora.

Serviços

{services.map(s => (
{s.name}
{s.desc}
{s.price == null ? "Sob consulta" : money(s.price)} {s.dur} {totalCb > 0 && s.price != null && }
onBook(s)} style={{ height: 42, padding: "0 16px", borderRadius: 12 }}>Agendar
))}
{videoOpen && p.video && setVideoOpen(false)} />} {reviewOpen && setReviewOpen(false)} onDone={() => API.company(companyId).catch(() => {})} />} {contactOpen && (
setContactOpen(false)} style={{ position: "absolute", inset: 0, background: "rgba(0,0,0,.5)", backdropFilter: "blur(3px)" }} />
Como quer entrar em contato?
{p.name}
)}
); } // ── Listing detail — fullscreen ─────────────────────────────── function ListingDetailSheet({ item, partner, onClose, onContact }) { const specs = (item._specs || item.specs || []).filter(s => s[0] !== "Segmento"); const segmento = (item._specs || item.specs || []).find(s => s[0] === "Segmento")?.[1] || null; const imgs = item.imgs && item.imgs.length ? item.imgs : (item.img && item.img.startsWith("http") ? [item.img] : []); const [imgIdx, setImgIdx] = React.useState(0); const [videoOpen, setVideoOpen] = React.useState(false); const [mapOpen, setMapOpen] = React.useState(false); const [contactNowOpen, setContactNowOpen] = React.useState(false); const hasImgs = imgs.length > 0; const hasVideo = !!item.videoUrl; const hasMap = item.lat != null && item.lng != null; const p = partner || {}; // Auto-advance das fotos a cada 3.8s React.useEffect(() => { if (imgs.length < 2) return; const t = setInterval(() => setImgIdx(i => (i + 1) % imgs.length), 3800); return () => clearInterval(t); }, [imgs.length]); return (
{/* ── Hero: imagens ── */}
{hasImgs ? imgs.map((url, i) => ( {item.title} )) : }
{/* botão fechar */} {/* contador de fotos — sempre no mesmo lugar, sem conflito com botão de vídeo */} {imgs.length > 1 && (
{imgIdx + 1} / {imgs.length}
)} {/* setas de navegação */} {imgs.length > 1 && (<> )} {/* título + preço + vídeo sobre a imagem */}
{segmento && ( {segmento} )}

{item.title}

{hasVideo && ( )}
{item.priceLabel}
{/* dots */} {imgs.length > 1 && (
{imgs.map((_, i) => (
setImgIdx(i)} style={{ width: i === imgIdx ? 20 : 6, height: 6, borderRadius: 999, background: i === imgIdx ? "var(--accent)" : "var(--line)", transition: "width .25s", cursor: "pointer" }} /> ))}
)}
{/* ── Conteúdo scrollável ── */}
1 ? 22 : 8 }}> {/* Specs em grid */} {specs.length > 0 && (
{specs.map(([k, v], i) => (
{k}
{v}
))}
)} {/* Botões: Ver no mapa + Solicitar contato lado a lado */}
{hasMap && ( )}
{/* Descrição */} {item.desc && (
Descrição
{item.urlAnuncio && ( Ver mais )}

{item.desc}

)} {/* "Ver mais informações" sem descrição */} {item.urlAnuncio && !item.desc && ( )}
{/* ── Modal vídeo fullscreen ── */} {videoOpen && setVideoOpen(false)} />} {/* ── Modal mapa localização ── */} {mapOpen && setMapOpen(false)} />} {/* ── Modal contato rápido ── */} {contactNowOpen && (
setContactNowOpen(false)} style={{ position: "absolute", inset: 0, background: "rgba(0,0,0,.5)", backdropFilter: "blur(3px)" }} />
Como quer entrar em contato?
{p.name || "Parceiro"} · {item.title}
{/* WhatsApp — prioritário */} {p.whatsapp && (
Falar pelo WhatsApp
Resposta mais rápida
)} {/* Ligar */} {p.phone && (
Ligar agora
{p.phone}
)} {/* Formulário de lead */}
)}
); } // ── Vitrine store ──────────────────────────────────────────── function VitrineStore({ partner, onBack, onLead, onNeedLogin }) { const companyId = partner._raw?.id || partner.id; const [detail, setDetail] = React.useState(null); React.useEffect(() => { if (!companyId) return; API.company(companyId).then(c => setDetail(apiCompanyToPartner(c))).catch(() => {}); }, [companyId]); const p = detail || partner; const [isFav, setIsFav] = React.useState(false); React.useEffect(() => { if (!companyId || !Auth.isLogged()) return; API.me.favorites().then(d => { setIsFav((d.favorites || []).some(f => f.id === companyId)); }).catch(() => {}); }, [companyId]); const toggleFav = () => { if (!Auth.isLogged()) { onNeedLogin?.(); return; } if (isFav) { API.me.removeFavorite(companyId).catch(() => {}); setIsFav(false); } else { API.me.addFavorite(companyId).catch(() => {}); setIsFav(true); } }; const [apiListings, setApiListings] = React.useState(null); React.useEffect(() => { API.listings(companyId).then(d => { const ls = (d.listings || []).filter(l => l.active).map(l => ({ id: l.id, title: l.title, desc: l.description || "", priceLabel: l.price_label || "Sob consulta", specs: l.specs || [], img: (l.image_urls && l.image_urls[0]) || null, imgs: l.image_urls || [], urlAnuncio: l.url_anuncio || null, lat: l.lat ?? null, lng: l.lng ?? null, videoUrl: l.video_url || null, _raw: l, })); if (ls.length) setApiListings(ls); }).catch(() => {}); }, [companyId]); const items = apiListings || catalogFor(p); const [detailItem, setDetailItem] = React.useState(null); const [videoOpen, setVideoOpen] = React.useState(false); const [contactOpen, setContactOpen] = React.useState(false); const [reviewOpen, setReviewOpen] = React.useState(false); const handleReview = () => { if (!Auth.isLogged()) { onNeedLogin?.(); return; } setReviewOpen(true); }; return (
{/* badge: só ícone, sem texto "Divulgação" */} } /> {p.blurb && (
{p.blurb}
)} {/* botão de vídeo */} {p.video && (
)} {/* Botões: Localização + Contato */}
{p.address && ( Localização )}
{p.horario && (
{p.horario}
)} {items.length > 0 &&

Em destaque

}
{items.map(it => ( ))}
{/* Listing detail bottom sheet */} {detailItem && ( setDetailItem(null)} onContact={() => { setDetailItem(null); onLead(detailItem); }} /> )} {videoOpen && p.video && setVideoOpen(false)} />} {reviewOpen && setReviewOpen(false)} />} {/* Modal contato rápido */} {contactOpen && (
setContactOpen(false)} style={{ position: "absolute", inset: 0, background: "rgba(0,0,0,.5)", backdropFilter: "blur(3px)" }} />
Como quer entrar em contato?
{p.name}
{p.whatsapp && (
Falar pelo WhatsApp
Resposta mais rápida
)} {p.phone && (
Ligar agora
{p.phone}
)}
)}
); } // ── Booking sheet ──────────────────────────────────────────── const WD = ["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb"]; const MO = ["jan", "fev", "mar", "abr", "mai", "jun", "jul", "ago", "set", "out", "nov", "dez"]; function nextDays(n) { const out = []; const d = new Date(); for (let i = 0; i < n; i++) { const x = new Date(d); x.setDate(d.getDate() + i); out.push(x); } return out; } function BookingSheet({ partner, service, onConfirm }) { const days = React.useMemo(() => nextDays(7), []); const [di, setDi] = React.useState(1); const [time, setTime] = React.useState(null); const [local, setLocal] = React.useState("domicilio"); const slots = ["08:00", "10:00", "13:00", "15:00", "17:00"]; const d = days[di]; const dateLabel = `${WD[d.getDay()]}, ${d.getDate()} ${MO[d.getMonth()]}`; return (
{service.name}
{partner.name} · {service.price == null ? "Sob consulta" : money(service.price)}
Data
{days.map((x, i) => ( ))}
Horário
{slots.map(s => setTime(s)}>{s})}
Onde
setLocal("domicilio")}>Na minha casa setLocal("loja")}>No estabelecimento
{ const iso = `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`; onConfirm({ dateISO: iso, dateLabel, time, local }); }}> {time ? `Confirmar · ${dateLabel} às ${time}` : "Escolha um horário"}
); } // ── Lead sheet (vitrine) ───────────────────────────────────── function LeadSheet({ partner, listing, onConfirm, user }) { const [pref, setPref] = React.useState("whats"); const [msg, setMsg] = React.useState(listing ? `Tenho interesse em: ${listing.title}` : "Tenho interesse e gostaria de mais informações."); const u = user || USER; return (
{partner.name}
Solicitar contato
{listing && (
{listing.img && listing.img.startsWith("http") ? {listing.title} : }
{listing.title}
{listing.priceLabel}
)}
Mensagem