(function (){
if(!window.BDMChatPages) window.BDMChatPages={};
var LS_PENDING_RESERVA="bdm_chat_pending_reserva";
var LS_PENDING_PAYMENT="bdm_chat_pending_payment";
var CUPOM_FIDELIDADE="FIDELIDADE";
var VALOR_REAL_POR_PONTO_DEFAULT=0.25;
var MESES=["janeiro", "fevereiro", "março", "abril", "maio", "junho", "julho", "agosto", "setembro", "outubro", "novembro", "dezembro"];
function pad2(n){
return n < 10 ? "0" + n:String(n);
}
function getMinSelectableMidnight(){
var n=new Date();
if(n.getHours()===23){
var t=new Date(n.getFullYear(), n.getMonth(), n.getDate() + 1);
return t;
}
return new Date(n.getFullYear(), n.getMonth(), n.getDate());
}
function isRealToday(y, m, d){
var t=new Date();
return y===t.getFullYear()&&m===t.getMonth()&&d===t.getDate();
}
function dateAtMidnight(y, m, d){
return new Date(y, m, d);
}
function isDateBeforeMin(selY, selM, selD, minDate){
if(!minDate||!(minDate instanceof Date)) return false;
return dateAtMidnight(selY, selM, selD).getTime() < minDate.getTime();
}
function isPrecosListEmpty(data){
if(!data) return true;
if(typeof data==="object"&&data.erro&&isPrecosUnavailableErroMessage(data.erro)){
return true;
}
return normalizePrecosList(data).length===0;
}
function isPrecosUnavailableErroMessage(msg){
var s=String(msg==null ? "":msg).trim().toLowerCase();
return s.indexOf("sem autorização")!==-1||s.indexOf("sem autorizacao")!==-1;
}
function normalizePrecosApiResponse(data){
if(!data||typeof data!=="object") return data;
if(data.erro&&isPrecosUnavailableErroMessage(data.erro)){
return [];
}
return data;
}
function syncBookingUnavailableUi(node, merged, base, state, minDate, booking){
var unSlot=node.querySelector('[data-slot="unavailable-notice"]');
var flow=node.querySelector('[data-slot="booking-flow"]');
var disp0=isDisp0Combined(merged, base);
var selToday=isRealToday(state.selY, state.selM, state.selD);
var precosEmpty = !!(state&&state.precosEmptyForDay);
var hideFlow=isDateBeforeMin(state.selY, state.selM, state.selD, minDate)||(disp0&&selToday);
var showMsg=(disp0&&selToday)||precosEmpty;
var msg="";
if(disp0&&selToday){
msg="Esta suíte está indisponível para hoje. Selecione a partir de amanhã no calendário para escolher permanência e horário de chegada.";
}else if(precosEmpty){
msg="Indisponível para o dia selecionado.";
}
if(unSlot){
unSlot.textContent=msg;
unSlot.hidden = !showMsg;
unSlot.classList.toggle("-hidden", !showMsg);
}
if(flow){
flow.hidden=hideFlow;
flow.classList.toggle("-hidden", hideFlow);
}
if(booking&&booking.syncTermsButton) booking.syncTermsButton();
if(booking&&booking.refresh) booking.refresh();
}
function isSuiteUnavailableToday(suite){
if(!suite||typeof suite!=="object") return false;
var readIntegracao =
typeof window.BDMChatReadIntegracaoFlag==="function"
? window.BDMChatReadIntegracaoFlag
: function (raw){
if(!raw||typeof raw!=="object") return "";
if(Object.prototype.hasOwnProperty.call(raw, "integracao")){
return String(raw.integracao==null ? "":raw.integracao).trim().toUpperCase();
}
return "";
};
if(readIntegracao(window.BDMChatMotelModel)==="N") return false;
if(readIntegracao(suite)==="N") return false;
if(typeof window.BDMChatSuiteUnavailableToday==="function"){
try {
return !!window.BDMChatSuiteUnavailableToday(suite);
} catch (e){
}}
var d=suite.disponibilidade;
if(d===0||d==="0"||d===false) return true;
if(typeof d==="string"){
var t=d.trim().toLowerCase();
if(t==="0"||t==="false"||t==="no"||t==="n"||t==="nao"||t==="não") return true;
if(t==="indisponivel"||t==="indisponível") return true;
}
if(suite.disponivel===false||suite.disponivel===0||suite.disponivel==="0"||suite.disponivel==="N"||suite.disponivel==="n") return true;
if(suite["disponível"]===false||suite["disponível"]===0||suite["disponível"]==="0") return true;
return false;
}
function isDisp0Combined(merged, base){
var mergedUnavailable=isSuiteUnavailableToday(merged);
var baseUnavailable=isSuiteUnavailableToday(base);
var result=mergedUnavailable||baseUnavailable;
try {
var readIntegracao =
typeof window.BDMChatReadIntegracaoFlag==="function"
? window.BDMChatReadIntegracaoFlag
: function (){ return ""; };} catch (e){}
return result;
}
function getRealTodayMidnight(){
var n=new Date();
return new Date(n.getFullYear(), n.getMonth(), n.getDate());
}
function getCalendarMinDateForSuite(merged, base){
if(isDisp0Combined(merged, base)){
return getRealTodayMidnight();
}
return getMinSelectableMidnight();
}
function sameCalendarDay(a, b){
if(!a||!b||!(a instanceof Date)||!(b instanceof Date)||isNaN(a.getTime())||isNaN(b.getTime())){
return false;
}
return (
a.getFullYear()===b.getFullYear() &&
a.getMonth()===b.getMonth() &&
a.getDate()===b.getDate()
);
}
function getInitialPrecosMinDate(base){
if(isSuiteUnavailableToday(base)){
return getRealTodayMidnight();
}
return getMinSelectableMidnight();
}
function unwrapDetalhesJson(raw){
if(!raw||typeof raw!=="object"||Array.isArray(raw)) return raw;
if(raw.erro!=null) return raw;
var hasTop =
Object.prototype.hasOwnProperty.call(raw, "suite") ||
Object.prototype.hasOwnProperty.call(raw, "detalhes") ||
Object.prototype.hasOwnProperty.call(raw, "disponibilidade") ||
Object.prototype.hasOwnProperty.call(raw, "suites");
if(hasTop) return raw;
if(raw.data&&typeof raw.data==="object"&&!Array.isArray(raw.data)) return raw.data;
return raw;
}
function buildMergedSuite(base, suiteApi, detJson){
var merged=Object.assign({}, base||{}, suiteApi||{});
applyAvailabilityFromRawResponse(detJson, merged);
if(!Object.prototype.hasOwnProperty.call(merged, "disponibilidade")){
applyAvailabilityFromRawResponse(base, merged);
}
return merged;
}
function isSuiteInterativaFlag(merged, base, detJson){
var rows=[merged, base, detJson];
var suiteApi=normalizeDetalhesPayload(detJson);
if(suiteApi&&typeof suiteApi==="object") rows.push(suiteApi);
if(detJson&&detJson.data&&typeof detJson.data==="object"&&!Array.isArray(detJson.data)){
rows.push(detJson.data);
}
for (var i=0; i < rows.length; i++){
var src=rows[i];
if(!src||typeof src!=="object") continue;
var v=String(src.interativa!=null ? src.interativa:"").trim().toUpperCase();
if(v==="S"||v==="SIM"||v==="1"||v==="TRUE") return true;
}
return false;
}
function selectDateForPrecosApi(state){
var now=new Date();
if(isRealToday(state.selY, state.selM, state.selD)){
return formatSelectDate(now);
}
return state.selY + "-" + pad2(state.selM + 1) + "-" + pad2(state.selD) + "T12:00:00";
}
function formatSelectDate(d){
if(!d||!(d instanceof Date)||isNaN(d.getTime())){
d=new Date();
}
return d.getFullYear() + "-" + pad2(d.getMonth() + 1) + "-" + pad2(d.getDate()) + "T" + pad2(d.getHours()) + ":" + pad2(d.getMinutes()) + ":" + pad2(d.getSeconds());
}
function buildReservaDateTimeFromState(state){
var now=new Date();
return (
state.selY + "-" + pad2(state.selM + 1) + "-" + pad2(state.selD) +
" " + pad2(now.getHours()) + ":" + pad2(now.getMinutes()) + ":" + pad2(now.getSeconds())
);
}
function periodoFromPrecoItem(p){
if(!p||typeof p!=="object") return "";
if(p.periodo!=null&&String(p.periodo).trim()) return String(p.periodo).trim();
if(p.duracao!=null&&String(p.duracao).trim()) return String(p.duracao).trim();
if(p.tempo!=null&&String(p.tempo).trim()) return String(p.tempo).trim();
if(p.horas!=null&&String(p.horas).trim()) return String(p.horas).trim();
return "";
}
function normalizePeriodoReserva(label){
var raw=String(label==null ? "":label).trim();
if(!raw) return "";
return raw.replace(/\s*horas?$/i, "").trim();
}
function normalizeDetalhesPayload(data){
if(Array.isArray(data)&&data.length&&data[0]&&typeof data[0]==="object"){
return data[0];
}
if(!data||typeof data!=="object") return {};
if(data.suite&&typeof data.suite==="object") return data.suite;
if(data.detalhes&&typeof data.detalhes==="object") return data.detalhes;
if(data.suites&&typeof data.suites==="object"&&!Array.isArray(data.suites)) return data.suites;
return data;
}
function applyAvailabilityFromRawResponse(raw, target){
if(!target||typeof target!=="object") return;
var blobs=[];
function add(b){
if(b&&typeof b==="object"&&!Array.isArray(b)&&blobs.indexOf(b) < 0) blobs.push(b);
}
if(!raw||typeof raw!=="object") return;
if(Array.isArray(raw)&&raw.length&&raw[0]&&typeof raw[0]==="object"){
add(raw[0]);
}else{
add(raw);
if(raw.data&&typeof raw.data==="object"&&!Array.isArray(raw.data)) add(raw.data);
if(raw.dados&&typeof raw.dados==="object"&&!Array.isArray(raw.dados)) add(raw.dados);
if(raw.result&&typeof raw.result==="object"&&!Array.isArray(raw.result)) add(raw.result);
if(raw.suite&&typeof raw.suite==="object") add(raw.suite);
if(raw.detalhes&&typeof raw.detalhes==="object") add(raw.detalhes);
}
for (var i=0; i < blobs.length; i++){
var b=blobs[i];
if(Object.prototype.hasOwnProperty.call(b, "disponibilidade")){
var v=b.disponibilidade;
if(v===false||v==="false") target.disponibilidade=0;
else if(v===true) target.disponibilidade=1;
else target.disponibilidade=v;
return;
}
if(Object.prototype.hasOwnProperty.call(b, "disponivel")||Object.prototype.hasOwnProperty.call(b, "disponível")){
var dv=b.disponivel!==undefined ? b.disponivel:b["disponível"];
target.disponibilidade=dv===false||dv===0||dv==="0"||dv==="N"||dv==="n" ? 0:1;
return;
}}
}
function looksLikePrecoItem(o){
if(!o||typeof o!=="object") return false;
return (
o.valor!=null||o.preco!=null||o.apartir!=null||o.price!=null ||
o.duracao!=null||o.tempo!=null||o.horas!=null||o.periodo!=null
);
}
function normalizePrecosList(data){
if(!data||typeof data!=="object") return [];
if(Array.isArray(data)){
if(!data.length) return [];
if(data.length > 1&&data.every(function (row){ return looksLikePrecoItem(row); })){
return data;
}
if(data.length===1&&data[0]&&typeof data[0]==="object"){
var fromWrapper=normalizePrecosList(data[0]);
if(fromWrapper.length) return fromWrapper;
}
if(looksLikePrecoItem(data[0])){
if(data.length===1||data.every(function (row){ return looksLikePrecoItem(row); })){
return data;
}}
for (var a=0; a < data.length; a++){
var innerArr=normalizePrecosList(data[a]);
if(innerArr.length) return innerArr;
}
return [];
}
if(looksLikePrecoItem(data)) return [data];
if(data.data&&typeof data.data==="object"){
var nestedData=normalizePrecosList(data.data);
if(nestedData.length) return nestedData;
}
var keys=["precos", "periodos", "tempos", "valores", "tarifas", "dados", "lista", "resultado"];
for (var i=0; i < keys.length; i++){
var block=data[keys[i]];
if(!block) continue;
var innerKey=normalizePrecosList(block);
if(innerKey.length) return innerKey;
}
if(data.suite&&typeof data.suite==="object"){
var innerSuite=normalizePrecosList(data.suite);
if(innerSuite.length) return innerSuite;
}
if(Array.isArray(data.suites)&&data.suites.length){
for (var s=0; s < data.suites.length; s++){
var innerSuites=normalizePrecosList(data.suites[s]);
if(innerSuites.length) return innerSuites;
}}
return [];
}
function imagesFromDetail(suite){
var out=[];
if(!suite||typeof suite!=="object") return out;
if(Array.isArray(suite.galeria)){
suite.galeria.forEach(function (g){
if(typeof g==="string"&&g) out.push({ image: g, thumb: g });
else if(g&&typeof g==="object"){
var u=g.image||g.imagem||g.url||g.src||"";
var th=g.thumbImage||g.thumb||u;
if(u) out.push({ image: u, thumb: th });
}});
if(out.length) return out;
}
if(suite.imagens){
var im=suite.imagens;
if(typeof im==="string"){
var t=im.trim();
if(t.indexOf("[")===0){
try {
var parsed=JSON.parse(t);
if(Array.isArray(parsed)){
parsed.forEach(function (item){
if(typeof item==="string"&&item) out.push({ image: item, thumb: item });
else if(item&&typeof item==="object"){
var u2=item.image||item.imagem||item.url||"";
var th2=item.thumbImage||item.thumb||u2;
if(u2) out.push({ image: u2, thumb: th2 });
}});
return out;
}} catch (e){  }}
if(t.indexOf(",")!==-1){
return t.split(",").map(function (x){ return x.trim(); }).filter(Boolean).map(function (u){ return { image: u, thumb: u };});
}
if(t) return [{ image: t, thumb: t }];
}
if(Array.isArray(im)){
im.forEach(function (item){
if(typeof item==="string"&&item) out.push({ image: item, thumb: item });
else if(item&&typeof item==="object"){
var u3=item.image||item.imagem||item.url||"";
var th3=item.thumbImage||item.thumb||u3;
if(u3) out.push({ image: u3, thumb: th3 });
}});
return out;
}}
return out;
}
function formatMoneyBR(n){
if(!isFinite(n)) return "R$ 0,00";
return "R$ " + n.toFixed(2).replace(".", ",");
}
function parseMoneyAny(v){
if(v==null||v==="") return NaN;
var s=String(v).trim().replace(/[^\d,.-]/g, "");
if(s.indexOf(",") >=0&&s.indexOf(".") >=0){
s=s.replace(/\./g, "").replace(",", ".");
}else if(s.indexOf(",") >=0){
s=s.replace(",", ".");
}
return parseFloat(s);
}
function toast(ctx, msg){
ctx.body.scrollTop=0;
ctx.body.appendChild(ctx.el("div", { class: "bdm-toast", text: msg }));
setTimeout(function (){
var t=ctx.body.querySelector(".bdm-toast");
if(t) t.remove();
}, 2200);
}
function labelFromPrecoItem(p, i){
if(typeof p!=="object"||!p) return "Opção " + (i + 1);
var label=p.label||p.texto||p.nome||p.descricao||"";
var dur=p.duracao!=null ? p.duracao:(p.tempo!=null ? p.tempo:(p.horas!=null ? p.horas:p.periodo));
if(!label&&dur!=null){
var ds=String(dur).trim();
label=ds;
}
if(!label) label="Opção " + (i + 1);
return normalizePeriodoReserva(label);
}
function renderGalleryFallback(ctx, slot, items){
var idx={ v: 0 };
var mainWrap=ctx.el("div", { class: "bdm-gallery-main" });
var mainImg=ctx.el("img", { class: "bdm-gallery-main-img", alt: "", loading: "lazy", referrerpolicy: "no-referrer" });
mainImg.src=items[0].image;
var prev=ctx.el("button", { class: "bdm-gallery-nav -prev", type: "button", "aria-label": "Anterior" });
prev.innerHTML='<i class="bdm-fa fa-thin fa-chevron-left" aria-hidden="true"></i>';
var next=ctx.el("button", { class: "bdm-gallery-nav -next", type: "button", "aria-label": "Próximo" });
next.innerHTML='<i class="bdm-fa fa-thin fa-chevron-right" aria-hidden="true"></i>';
function show(i){
if(i < 0) i=items.length - 1;
if(i >=items.length) i=0;
idx.v=i;
mainImg.src=items[i].image;
}
prev.addEventListener("click", function (e){ e.stopPropagation(); show(idx.v - 1); });
next.addEventListener("click", function (e){ e.stopPropagation(); show(idx.v + 1); });
mainWrap.appendChild(mainImg);
mainWrap.appendChild(prev);
mainWrap.appendChild(next);
slot.appendChild(mainWrap);
var thumbs=ctx.el("div", { class: "bdm-gallery-thumbs" });
items.forEach(function (it, i){
var b=ctx.el("button", { class: "bdm-gallery-thumb", type: "button" });
b.appendChild(ctx.el("img", { src: it.thumb, alt: "", loading: "lazy", referrerpolicy: "no-referrer" }));
b.addEventListener("click", function (e){ e.stopPropagation(); show(i); });
thumbs.appendChild(b);
});
slot.appendChild(thumbs);
}
function renderGallery(ctx, slot, items){
slot.innerHTML="";
if(!items||!items.length){
slot.appendChild(ctx.el("div", { class: "bdm-gallery-empty", text: "Sem imagens." }));
return;
}
var jq=window.jQuery;
if(!jq||typeof jq.fn.owlCarousel!=="function"||typeof jq.fn.lightGallery!=="function"){
renderGalleryFallback(ctx, slot, items);
return;
}
var uid="bdm-gallery-" + String(Date.now()) + "-" + String(Math.random()).slice(2, 9);
var $slot=jq(slot);
var $wrap=jq("<div/>", {
class: "bdm-gallery-wrap",
"data-wp-ignore": "",
"data-bdm-gallery": "1"
});
var $carousel=jq("<div/>", { class: "owl-carousel bdm-gallery-owl", id: uid });
items.forEach(function (it){
var src=it.image;
var th=it.thumb||src;
var $a=jq("<a/>", {
class: "bdm-lg-item",
href: src,
"data-src": src,
"data-exthumbimage": th
});
$a.append(jq("<img/>", { src: th, alt: "", loading: "lazy", referrerpolicy: "no-referrer", draggable: false }));
$carousel.append(jq("<div/>", { class: "item" }).append($a));
});
$wrap.append($carousel);
$slot.append($wrap);
$wrap.get(0).addEventListener("click",
function (ev){
var el=ev.target&&ev.target.closest&&ev.target.closest("a.bdm-lg-item");
if(el) ev.preventDefault();
},
true
);
$carousel.owlCarousel({
loop: false,
margin: 0,
nav: items.length > 1,
navElement: "button",
navText: [
'<span class="bdm-owl-nav-inner"><i class="bdm-fa fa-thin fa-chevron-left" aria-hidden="true"></i></span>',
'<span class="bdm-owl-nav-inner"><i class="bdm-fa fa-thin fa-chevron-right" aria-hidden="true"></i></span>'
],
dots: false,
items: 1
});
$carousel.find(".owl-nav button").attr("type", "button");
$carousel.lightGallery({
selector: "a.bdm-lg-item",
download: false,
thumbnail: true,
showThumbByDefault: true,
animateThumb: true,
currentPagerPosition: "middle",
thumbWidth: 96,
thumbHeight: "72px",
thumbMargin: 8,
thumbContHeight: 100,
hideBarsDelay: 3000,
fullScreen: true,
hideControlOnEnd: true
});
}
var DOW_CODE_TO_NUM={ dom: 0, seg: 1, ter: 2, qua: 3, qui: 4, sex: 5, sab: 6 };
function parseHmToMinutes(hm){
var s=String(hm==null ? "":hm).trim();
var m=s.match(/^(\d{1,2}):(\d{2})/);
if(!m) return 0;
return parseInt(m[1], 10) * 60 + parseInt(m[2], 10);
}
function dowNumFromIntervalCode(code){
var c=String(code==null ? "":code).trim().toLowerCase().substring(0, 3);
return Object.prototype.hasOwnProperty.call(DOW_CODE_TO_NUM, c) ? DOW_CODE_TO_NUM[c]:-1;
}
function isArrivalBlockedByIntervalo(selY, selM, selD, slotHm, intervalo){
if(!intervalo||typeof intervalo!=="object") return false;
var selDow=new Date(selY, selM, selD).getDay();
var selMin=parseHmToMinutes(slotHm);
var startDow=dowNumFromIntervalCode(intervalo.dia_semana_inicio);
var endDow=dowNumFromIntervalCode(intervalo.dia_semana_fim);
var startMin=parseHmToMinutes(intervalo.hora_inicio);
var endMin=parseHmToMinutes(intervalo.hora_fim);
if(startDow < 0||endDow < 0) return false;
if(startDow===endDow){
if(selDow!==startDow) return false;
if(startMin <=endMin){
return selMin >=startMin&&selMin < endMin;
}
return selMin >=startMin||selMin < endMin;
}
if(startDow < endDow){
if(selDow < startDow||selDow > endDow) return false;
if(selDow===startDow) return selMin >=startMin;
if(selDow===endDow) return selMin < endMin;
return true;
}
if(selDow===startDow) return selMin >=startMin;
if(selDow===endDow) return selMin < endMin;
if(selDow > startDow||selDow < endDow) return true;
return false;
}
function isArrivalSlotBlocked(state, slotHm, intervalos){
if(!intervalos||!intervalos.length) return false;
for (var i=0; i < intervalos.length; i++){
if(isArrivalBlockedByIntervalo(state.selY, state.selM, state.selD, slotHm, intervalos[i])){
return true;
}}
return false;
}
function normalizeIntervalosList(json){
if(!json) return [];
if(Array.isArray(json)) return json;
if(typeof json!=="object") return [];
if(json.erro) return [];
if(Array.isArray(json.data)) return json.data;
if(Array.isArray(json.intervalos)) return json.intervalos;
if(Array.isArray(json.intervalos_dia_semana)) return json.intervalos_dia_semana;
return [];
}
function buildArrivalSlots(state, intervalos){
var now=new Date();
var slots=[];
if(isRealToday(state.selY, state.selM, state.selD)){
var start=new Date(now.getTime());
start.setSeconds(0, 0);
start.setMinutes(0);
start.setHours(start.getHours() + 1);
if(start.getFullYear()!==state.selY ||
start.getMonth()!==state.selM ||
start.getDate()!==state.selD
){
return slots;
}
var h0=start.getHours();
for (var h=h0; h <=23; h++){
slots.push(pad2(h) + ":00");
}}else{
for (var h2=0; h2 <=23; h2++){
slots.push(pad2(h2) + ":00");
}}
if(!intervalos||!intervalos.length) return slots;
return slots.filter(function (hm){
return !isArrivalSlotBlocked(state, hm, intervalos);
});
}
function fillChegada(select, state, intervalos){
select.innerHTML="";
var horarios=buildArrivalSlots(state, intervalos);
var ph=document.createElement("option");
ph.value="";
ph.textContent="Selecionar";
select.appendChild(ph);
horarios.forEach(function (h, i){
var opt=document.createElement("option");
opt.value=String(i + 1);
opt.textContent=h;
opt._bdmH=h;
select.appendChild(opt);
});
select.value="";
}
function fillPermanencia(select, precosArr, fallbackValor){
select.innerHTML="";
var ph=document.createElement("option");
ph.value="";
ph.textContent="Selecionar";
select.appendChild(ph);
var opts=[];
if(precosArr&&precosArr.length){
precosArr.forEach(function (p, i){
var label=labelFromPrecoItem(p, i);
var val=parseMoneyAny(typeof p==="object"&&p ? (p.valor||p.preco||p.apartir||p.price):"");
var periodoApi=periodoFromPrecoItem(p)||label;
opts.push({ label: label, valor: isFinite(val) ? val:NaN, raw: p, periodoApi: periodoApi });
});
}else{
opts.push({ label: "02:00", valor: fallbackValor, raw: null, periodoApi: "02:00" });
}
opts.forEach(function (o, i){
var opt=document.createElement("option");
opt.value=String(i);
opt.textContent=o.label;
opt._bdm=o;
select.appendChild(opt);
});
select.value="";
}
function renderCalendar(ctx, slot, state, minDate, onChange){
var minY=minDate.getFullYear();
var minM=minDate.getMonth();
var minD=minDate.getDate();
function isCellDisabled(y, m, d){
var cell=new Date(y, m, d);
return cell < minDate;
}
function draw(){
slot.innerHTML="";
var y=state.calYear;
var m=state.calMonth;
var first=new Date(y, m, 1);
var startWeek=first.getDay();
var daysInMonth=new Date(y, m + 1, 0).getDate();
var head=ctx.el("div", { class: "bdm-cal-head" });
var prevM=ctx.el("button", { class: "bdm-cal-nav", type: "button", "aria-label": "Mês anterior" });
prevM.innerHTML='<i class="bdm-fa fa-thin fa-chevron-left" aria-hidden="true"></i>';
var nextM=ctx.el("button", { class: "bdm-cal-nav", type: "button", "aria-label": "Próximo mês" });
nextM.innerHTML='<i class="bdm-fa fa-thin fa-chevron-right" aria-hidden="true"></i>';
var title=ctx.el("div", { class: "bdm-cal-title" }, [MESES[m] + " de " + y]);
var firstOfCal=new Date(y, m, 1);
var firstOfMin=new Date(minY, minM, 1);
var atMinMonth=firstOfCal.getTime() <=firstOfMin.getTime();
if(atMinMonth){
prevM.disabled=true;
prevM.classList.add("-disabled");
}
prevM.addEventListener("click", function (){
if(atMinMonth) return;
if(m===0){ m=11; y--; }else{ m--; }
state.calYear=y;
state.calMonth=m;
draw();
});
nextM.addEventListener("click", function (){
if(m===11){ m=0; y++; }else{ m++; }
state.calYear=y;
state.calMonth=m;
draw();
});
head.appendChild(prevM);
head.appendChild(title);
head.appendChild(nextM);
slot.appendChild(head);
var grid=ctx.el("div", { class: "bdm-cal-grid" });
var dow=["D", "S", "T", "Q", "Q", "S", "S"];
for (var i=0; i < 7; i++) grid.appendChild(ctx.el("div", { class: "bdm-cal-dow", text: dow[i] }));
for (var p=0; p < startWeek; p++) grid.appendChild(ctx.el("div", { class: "bdm-cal-cell -empty" }));
for (var d=1; d <=daysInMonth; d++){
(function (day){
var dis=isCellDisabled(y, m, day);
var cell=ctx.el("button", { class: "bdm-cal-cell", type: "button", text: String(day) });
if(dis){
cell.classList.add("-disabled");
cell.disabled=true;
}else if(state.selY===y&&state.selM===m&&state.selD===day){
cell.classList.add("-selected");
}
if(!dis){
cell.addEventListener("click", function (){
state.selY=y;
state.selM=m;
state.selD=day;
draw();
if(onChange) onChange();
});
}
grid.appendChild(cell);
})(d);
}
slot.appendChild(grid);
}
draw();
}
function createFidelidadeState(){
return {
saldo: null,
programaAtivo: false,
valorRealPorPonto: VALOR_REAL_POR_PONTO_DEFAULT,
pontosPorRealResgate: 0,
valorBrutoPeriodo: 0,
valorTotal: 0,
descontoCupom: 0,
cupomManual: "",
cupom: "",
usarPontos: false,
aplicandoCupom: false,
tipoDesconto: "",
descontoLabel: "",
pontosUtilizados: 0,
ocultarCampoCupom: false
};}
function pickApiNumber(obj, keys){
for (var i=0; i < keys.length; i++){
var v=obj&&obj[keys[i]];
if(v==null||v==="") continue;
var n=parseFloat(String(v).replace(/\./g, "").replace(",", "."));
if(isFinite(n)) return n;
}
return NaN;
}
function pontosNecessariosReserva(bruto, valorPorPonto, pontosPorRealResgate){
var b=Number(bruto)||0;
if(b <=0) return 0;
var porReal=Number(pontosPorRealResgate)||0;
if(porReal > 0) return Math.ceil(b * porReal);
var rate=Number(valorPorPonto)||VALOR_REAL_POR_PONTO_DEFAULT;
if(rate <=0) return 0;
return Math.ceil(b / rate);
}
function parseCupomAplicarResponse(api, valorBruto){
if(!api||typeof api!=="object") return null;
var result=String(api.result||api.status||"").trim().toLowerCase();
if(result&&result!=="success"&&result!=="ok"){
return { erro: String(api.erro||api.message||"Cupom inválido.") };}
var bruto=Number(valorBruto)||0;
var total=pickApiNumber(api, ["valor_reserva", "valor_final", "valor_total", "total", "valor", "selectTotal"]);
var tipoDesconto=String(api.tipo_desconto||"").trim().toLowerCase();
var valorDescontoRaw=pickApiNumber(api, ["valor_desconto", "desconto", "desconto_cupom"]);
var descontoValor=0;
var descontoLabel="";
if(tipoDesconto.indexOf("porcent") >=0||tipoDesconto==="percent"||tipoDesconto==="%"){
var pct=isFinite(valorDescontoRaw) ? valorDescontoRaw:0;
if(bruto > 0&&pct > 0){
descontoValor=Math.round(((bruto * pct) / 100) * 100) / 100;
}
descontoLabel=String(Math.round(pct)) + "%";
}else if(isFinite(valorDescontoRaw)&&valorDescontoRaw > 0){
descontoValor=valorDescontoRaw;
descontoLabel=formatMoneyBR(descontoValor);
}
if(!isFinite(total)){
total=Math.max(0, bruto - descontoValor);
}
if(!isFinite(descontoValor)||descontoValor <=0){
descontoValor=Math.max(0, bruto - total);
}
var pontosUtil=pickApiNumber(api, ["pontos_utilizados", "pontos_usados"]);
if(!isFinite(pontosUtil)) pontosUtil=0;
return {
total: isFinite(total) ? total:0,
desconto: descontoValor,
tipoDesconto: tipoDesconto,
descontoLabel: descontoLabel,
pontosUtilizados: Math.floor(pontosUtil),
ocultarCampoCupom:
api.ocultar_campo_cupom===true ||
api.ocultar_campo_cupom===1 ||
api.ocultar_campo_cupom==="1"
};}
function applyCupomParsedToFidelidade(fid, parsed, bruto){
if(!parsed||!fid) return;
fid.valorTotal=parsed.total;
fid.descontoCupom=parsed.desconto;
fid.tipoDesconto=parsed.tipoDesconto||"";
fid.descontoLabel=parsed.descontoLabel||"";
fid.pontosUtilizados=parsed.pontosUtilizados||0;
fid.ocultarCampoCupom = !!parsed.ocultarCampoCupom;
if(fid.descontoCupom <=0&&bruto > 0){
fid.descontoCupom=Math.max(0, bruto - parsed.total);
}}
function fidelidadeCupomAtivo(fid){
if(!fid) return false;
if(fid.usarPontos) return true;
return String(fid.cupomManual||"").trim().length > 0;
}
function limparDescontoFidelidadeState(fid, bruto){
if(!fid) return;
fid.usarPontos=false;
fid.aplicandoCupom=false;
fid.cupom="";
fid.cupomManual="";
fid.descontoCupom=0;
fid.tipoDesconto="";
fid.descontoLabel="";
fid.pontosUtilizados=0;
fid.ocultarCampoCupom=false;
var b=Number(bruto);
if(isFinite(b)&&b > 0) fid.valorTotal=b;
}
function carregarSaldoFidelidade(ctx, fid){
var pages=window.BDMChatPages||{};
var uid=pages.getUsuarioId ? pages.getUsuarioId():0;
var url=(ctx.urls.pontosFidelidade||"").trim();
if(!uid||!url){
fid.saldo=null;
return Promise.resolve();
}
var saldoPromise=pages.fetchWidgetJson
? pages.fetchWidgetJson(ctx, "pontosSaldo", { id_usuario: uid }, url)
: fetch(url, {
method: "POST",
credentials: "same-origin",
headers: pages.mergeWidgetAuthHeaders({ "Content-Type": "application/json" }),
body: JSON.stringify({ id_usuario: uid })
}).then(function (r){ return r.json(); });
return saldoPromise.then(function (api){
if(!api||typeof api!=="object"){
fid.saldo=null;
return;
}
fid.programaAtivo=api.programa_ativo!==false&&api.programa_ativo!==0&&api.programa_ativo!=="0";
fid.saldo=pickApiNumber(api, ["saldo", "pontos", "pontos_saldo"]);
if(!isFinite(fid.saldo)) fid.saldo=0;
fid.saldo=Math.floor(fid.saldo);
var block=api.regras;
if(block&&typeof block==="object"&&!Array.isArray(block)){
var rate=pickApiNumber(block, ["valor_ponto_reais", "valorRealPorPonto"]);
if(isFinite(rate)&&rate > 0) fid.valorRealPorPonto=rate;
var porReal=pickApiNumber(block, ["pontos_por_real_em_resgate", "pontosPorRealResgate"]);
if(isFinite(porReal)&&porReal > 0) fid.pontosPorRealResgate=porReal;
}})
.catch(function (){
fid.saldo=null;
});
}
function updateResumo(ctx, node, state, selPerm, selChegada){
var dataEl=node.querySelector('[data-bind-resumo="data"]');
var chegadaEl=node.querySelector('[data-bind-resumo="chegada"]');
var periodoEl=node.querySelector('[data-bind-resumo="periodo"]');
var totalEl=node.querySelector('[data-slot="total"]');
var descontoRow=node.querySelector('[data-booking="desconto-row"]');
var descontoVal=node.querySelector('[data-booking="desconto-valor"]');
var fid=state.fidelidade;
if(dataEl) dataEl.textContent=pad2(state.selD) + "-" + pad2(state.selM + 1) + "-" + state.selY;
if(chegadaEl) chegadaEl.textContent=selChegada||"--:--";
if(periodoEl) periodoEl.textContent=selPerm ? selPerm.label:"--";
var bruto=selPerm&&isFinite(selPerm.valor) ? selPerm.valor:state.fallbackValor;
if(fid){
if(selPerm&&isFinite(selPerm.valor)&&selPerm.valor > 0){
fid.valorBrutoPeriodo=selPerm.valor;
if(!fidelidadeCupomAtivo(fid)){
limparDescontoFidelidadeState(fid, selPerm.valor);
}}else{
fid.valorBrutoPeriodo=0;
if(!fidelidadeCupomAtivo(fid)){
limparDescontoFidelidadeState(fid, 0);
}}
}
var exibirTotal=bruto;
if(fid&&fidelidadeCupomAtivo(fid)&&isFinite(fid.valorTotal)){
exibirTotal=fid.valorTotal;
}
if(totalEl){
if(selPerm&&isFinite(selPerm.valor)) totalEl.textContent=formatMoneyBR(exibirTotal);
else if(isFinite(state.fallbackValor)) totalEl.textContent=formatMoneyBR(state.fallbackValor);
}
if(descontoRow&&descontoVal&&fid){
var cupomAtivo=fidelidadeCupomAtivo(fid);
var showDesc =
cupomAtivo&&(fid.descontoCupom > 0||(fid.descontoLabel&&String(fid.descontoLabel).trim().length > 0));
if(showDesc){
if(fid.tipoDesconto&&fid.tipoDesconto.indexOf("porcent") >=0&&fid.descontoLabel){
descontoVal.textContent="- " + fid.descontoLabel;
}else if(fid.descontoCupom > 0){
descontoVal.textContent="- " + formatMoneyBR(fid.descontoCupom);
}else{
descontoVal.textContent="-";
}
descontoRow.removeAttribute("hidden");
}else{
descontoVal.textContent="";
descontoRow.setAttribute("hidden", "hidden");
}}
if(fid&&typeof state.syncFidelidadeUi==="function") state.syncFidelidadeUi();
}
function setReservarEnabled(btn, on){
if(!btn) return;
btn.disabled = !on;
if(on) btn.classList.remove("-disabled");
else btn.classList.add("-disabled");
}
function lockBookingSelects(selPerm, selChe){
if(!selPerm||!selChe) return;
selPerm.disabled=true;
selChe.disabled=true;
selPerm.innerHTML="";
var o1=document.createElement("option");
o1.value="";
o1.textContent="Indisponível";
selPerm.appendChild(o1);
selChe.innerHTML="";
var o2=document.createElement("option");
o2.value="";
o2.textContent="Indisponível";
selChe.appendChild(o2);
}
function previewForLog(v, max){
max=max||500;
var s=String(v==null ? "":v);
if(s.length > max) return s.slice(0, max) + "… (+" + (s.length - max) + " caracteres)";
return s;
}
function shallowTermoFields(obj){
if(!obj||typeof obj!=="object") return {};
var out={};
Object.keys(obj).forEach(function (k){
if(!/termo/i.test(k)) return;
var v=obj[k];
out[k]=typeof v==="string" ? previewForLog(v, 600):v;
});
return out;
}
function logTermosDebug(merged, base, detJson, context){
var model=window.BDMChatMotelModel||{};
var motel=model&&model.motel&&typeof model.motel==="object" ? model.motel:null;
var payload={
onde: context||"",
merged: shallowTermoFields(merged),
base: shallowTermoFields(base),
motel: motel ? shallowTermoFields(motel):{},
model: shallowTermoFields(model)
};
if(detJson&&typeof detJson==="object"){
payload.detalhes_raiz=shallowTermoFields(detJson);
if(detJson.data&&typeof detJson.data==="object") payload.detalhes_data=shallowTermoFields(detJson.data);
if(detJson.suite&&typeof detJson.suite==="object") payload.detalhes_suite=shallowTermoFields(detJson.suite);
if(detJson.detalhes&&typeof detJson.detalhes==="object") payload.detalhes_nested=shallowTermoFields(detJson.detalhes);
}
var urls=[];
function tryUrl(s){
var t=String(s==null ? "":s).trim();
if(/^https?:\/\//i.test(t)&&urls.indexOf(t) < 0) urls.push(t);
}
[merged, base, motel, model, detJson, detJson&&detJson.data, detJson&&detJson.suite, detJson&&detJson.detalhes].forEach(function (o){
if(!o||typeof o!=="object") return;
Object.keys(o).forEach(function (k){
if(!/termo/i.test(k)) return;
tryUrl(o[k]);
});
});
urls.forEach(function (url){
fetch(url, { credentials: "omit", mode: "cors" })
.then(function (r){
return r.text().then(function (t){});
})
.catch(function (err){
console.log("[BDM chat termos] erro ao buscar URL (CORS/rede?)", url, err);
});
});
}
function sanitizeTermosHtml(rawHtml){
var source=String(rawHtml==null ? "":rawHtml).trim();
if(!source) return "";
var root=document.createElement("div");
root.innerHTML=source;
var blocked=root.querySelectorAll("script,style,iframe,object,embed,link,meta,base,form,input,textarea,select,button");
for (var i=0; i < blocked.length; i++){
var n=blocked[i];
if(n&&n.parentNode) n.parentNode.removeChild(n);
}
var all=root.querySelectorAll("*");
for (var j=0; j < all.length; j++){
var el=all[j];
for (var k=el.attributes.length - 1; k >=0; k--){
var attr=el.attributes[k];
var name=String(attr.name||"").toLowerCase();
var value=String(attr.value||"");
if(name.indexOf("on")===0){
el.removeAttribute(attr.name);
continue;
}
if((name==="href"||name==="src"||name==="xlink:href")&&/^\s*javascript:/i.test(value)){
el.removeAttribute(attr.name);
}}
}
return root.innerHTML.trim();
}
function readSuiteTermsHtml(merged, base){
var model=window.BDMChatMotelModel||{};
var motel=model&&model.motel&&typeof model.motel==="object" ? model.motel:null;
var sources=[merged, base, motel, model];
for (var i=0; i < sources.length; i++){
var src=sources[i];
if(!src||typeof src!=="object") continue;
if(Object.prototype.hasOwnProperty.call(src, "termos")){
var rawT=src.termos;
var sanitized=sanitizeTermosHtml(rawT);
if(sanitized) return sanitized;
if(rawT!=null&&String(rawT).trim()!==""){
console.log("[BDM chat termos] campo `termos` existe mas ficou vazio após sanitize (não é HTML útil?)", {
origem: i,
tipo: typeof rawT,
preview: previewForLog(rawT, 300)
});
}}
}
return "";
}
function extractTermosFromRestJson(json){
if(!json) return "";
if(Array.isArray(json)){
for (var a=0; a < json.length; a++){
var row=json[a];
if(row&&typeof row==="object"&&typeof row.termos==="string"&&row.termos.trim()!==""){
return row.termos;
}}
return "";
}
if(typeof json!=="object") return "";
if(json.erro) return "";
var keys=["termos", "texto", "conteudo", "html", "content", "body"];
var i;
for (i=0; i < keys.length; i++){
var k=keys[i];
if(Object.prototype.hasOwnProperty.call(json, k)){
var v=json[k];
if(typeof v==="string"&&v.trim()!=="") return v;
}}
var data=json.data;
if(data&&typeof data==="object"){
for (i=0; i < keys.length; i++){
var k2=keys[i];
if(Object.prototype.hasOwnProperty.call(data, k2)){
var v2=data[k2];
if(typeof v2==="string"&&v2.trim()!=="") return v2;
}}
}
if(typeof data==="string"&&data.trim()!=="") return data;
return "";
}
function fetchPainelTermosJquery(panelUrl){
var bdm=window.motelswingChat||{};
var url=String(panelUrl||bdm.panelTermosAbsUrl||"").trim();
if(typeof window.jQuery==="undefined"||!url){
return Promise.resolve(null);
}
var $=window.jQuery;
return new Promise(function (resolve){
$.ajax({
url: url,
type: "POST",
dataType: "text",
timeout: 30000
})
.done(function (text){
var s=typeof text==="string" ? text:String(text==null ? "":text);
var parsed=null;
try {
parsed=s ? JSON.parse(s):null;
} catch (e){
parsed=null;
}
if(parsed&&typeof parsed==="object"){
resolve(parsed);
return;
}
if(s.trim()!==""){
resolve({ termos: s });
return;
}
resolve(null);
})
.fail(function (xhr, status, err){
console.log("[BDM chat termos] $.ajax POST ao painel falhou:", status, err, xhr&&xhr.status);
resolve(null);
});
});
}
function resolveTermsModalHost(hostNode){
if(!hostNode||!hostNode.closest) return hostNode;
var openPanel=hostNode.closest(".bdm-chat-panel.is-open");
if(openPanel) return openPanel;
var panel=hostNode.closest(".bdm-chat-panel");
if(panel) return panel;
var root=hostNode.closest("#bdm-chat-container");
return root||hostNode;
}
function closeTermsModal(hostNode){
var rootHost=resolveTermsModalHost(hostNode);
if(!rootHost||!rootHost.querySelector) return;
var modal=rootHost.querySelector(".bdm-terms-modal-backdrop");
if(modal&&modal.parentNode){
modal.parentNode.removeChild(modal);
}}
function fetchTermosRest(ctx, restUrl){
var url=String(restUrl||"").trim();
if(!url) return Promise.resolve(null);
return fetch(url, {
method: "POST",
credentials: "same-origin",
headers: window.BDMChatPages.mergeWidgetAuthHeaders({ "Content-Type": "application/json" }),
body: "{}"
})
.then(function (r){
return r.json();
})
.catch(function (){
return null;
});
}
function openTermsModal(hostNode, termsHtml, onAccept, modalTitle){
if(!hostNode) return;
var rootHost=resolveTermsModalHost(hostNode);
if(!rootHost) return;
closeTermsModal(rootHost);
var backdrop=document.createElement("div");
backdrop.className="bdm-terms-modal-backdrop";
var panel=document.createElement("div");
panel.className="bdm-terms-modal";
var head=document.createElement("div");
head.className="bdm-terms-modal-head";
var title=document.createElement("h3");
title.className="bdm-terms-modal-title";
title.textContent=modalTitle||"Termos de uso";
var acceptBtn=document.createElement("button");
acceptBtn.type="button";
acceptBtn.className="bdm-terms-modal-accept";
acceptBtn.textContent="Aceito";
var body=document.createElement("div");
body.className="bdm-terms-modal-body";
body.innerHTML=termsHtml||"<p>Termos indisponíveis no momento.</p>";
head.appendChild(title);
head.appendChild(acceptBtn);
panel.appendChild(head);
panel.appendChild(body);
backdrop.appendChild(panel);
rootHost.appendChild(backdrop);
acceptBtn.addEventListener("click", function (e){
e.preventDefault();
e.stopPropagation();
console.log("[BDM chat termos] botão Aceito no modal", {
termsHtmlLength: String(termsHtml||"").length,
preview: previewForLog(termsHtml, 200)
});
if(typeof onAccept==="function") onAccept();
closeTermsModal(rootHost);
});
}
function bindBooking(ctx, node, state, precosPayload, precosArr, minDate, merged, base, detJson, intervalosFechamento, idSuite){
state.intervalosFechamento=intervalosFechamento&&intervalosFechamento.length ? intervalosFechamento:[];
state.fidelidade=createFidelidadeState();
var fid=state.fidelidade;
var selPerm=node.querySelector('[data-booking="permanencia"]');
var selChe=node.querySelector('[data-booking="chegada"]');
var cupomInp=node.querySelector('[data-booking="cupom"]');
var cupomWrap=node.querySelector('[data-booking="cupom-wrap"]');
var cupomApplyBtn=node.querySelector('[data-booking="cupom-apply"]');
var fidelidadeBox=node.querySelector('[data-slot="fidelidade-reserva-box"]');
var fidelidadeCheck=node.querySelector('[data-booking="fidelidade-check"]');
var cupomApplySeq=0;
var fidelidadeHint=node.querySelector('[data-booking="fidelidade-hint"]');
var termos=node.querySelector('[data-booking="termos"]');
var applyBtn=cupomApplyBtn||node.querySelector(".bdm-booking-apply");
var termLink=node.querySelector(".bdm-booking-terms-link");
var interacaoSection=node.querySelector('[data-slot="interacao-section"]');
var interacaoTermosWrap=node.querySelector('[data-slot="interacao-termos-wrap"]');
var interacaoCheck=node.querySelector('[data-booking="interacao-check"]');
var termosInteracaoCheck=node.querySelector('[data-booking="termos-interacao-check"]');
var resBtn=node.querySelector("[data-suite-reservar]");
var suiteInterativa=isSuiteInterativaFlag(merged, base, detJson);
state.suiteInterativa=suiteInterativa;
state.interacaoChecked=false;
state.termosInteracaoChecked=false;
var termsHtml=readSuiteTermsHtml(merged, base);
logTermosDebug(merged, base, detJson, "ao montar reserva (suite-detalhes)");
function syncInteracaoUi(){
if(!interacaoSection) return;
if(suiteInterativa){
interacaoSection.removeAttribute("hidden");
}else{
interacaoSection.setAttribute("hidden", "hidden");
state.interacaoChecked=false;
state.termosInteracaoChecked=false;
if(interacaoCheck) interacaoCheck.checked=false;
if(termosInteracaoCheck) termosInteracaoCheck.checked=false;
}
if(interacaoTermosWrap){
if(state.interacaoChecked) interacaoTermosWrap.removeAttribute("hidden");
else {
interacaoTermosWrap.setAttribute("hidden", "hidden");
state.termosInteracaoChecked=false;
if(termosInteracaoCheck) termosInteracaoCheck.checked=false;
}}
syncTermsButton();
}
function openTermosInterativaModalComFallback(hostNode, onAcceptCb){
fetchTermosRest(ctx, ctx.urls.suiteTermosInterativa)
.then(function (json){
var raw=extractTermosFromRestJson(json);
if(raw&&String(raw).trim()) return raw;
var bdm=window.motelswingChat||{};
return fetchPainelTermosJquery(bdm.panelTermosInterativaAbsUrl).then(function (j2){
return extractTermosFromRestJson(j2);
});
})
.then(function (raw){
if(!raw||!String(raw).trim()){
toast(ctx, "Termos de interação indisponíveis no momento.");
return;
}
var safe=sanitizeTermosHtml(raw);
if(!safe){
toast(ctx, "Termos de interação indisponíveis no momento.");
return;
}
openTermsModal(hostNode, safe, onAcceptCb, "Termos de uso da interação");
});
}
function openTermosModalComFallback(hostNode, onAcceptCb){
var local=readSuiteTermsHtml(merged, base);
if(local){
openTermsModal(hostNode, local, onAcceptCb);
return;
}
fetchPainelTermosJquery().then(function (json){
var raw=extractTermosFromRestJson(json);
if(!raw||!String(raw).trim()){
logTermosDebug(merged, base, detJson, "termos — POST ao painel sem HTML (CORS bloqueou? token?)");
toast(ctx, "Termos de uso indisponíveis no momento.");
return;
}
merged.termos=raw;
var safe=sanitizeTermosHtml(raw);
if(!safe){
toast(ctx, "Termos de uso indisponíveis no momento.");
return;
}
openTermsModal(hostNode, safe, onAcceptCb);
});
}
if(!termsHtml){
console.log("[BDM chat termos] sem HTML ao montar — link/checkbox fazem POST (jQuery) ao painel se precisar.");
}
var fb=state.fallbackValor;
var lastPrecosArr=precosArr&&precosArr.length ? precosArr.slice():[];
function getValorBrutoPeriodoAtual(){
var p=currentPermOption();
if(!p||!isFinite(p.valor)||p.valor <=0) return 0;
return Number(p.valor);
}
function getPontosNecessariosAtual(){
var bruto=getValorBrutoPeriodoAtual();
return pontosNecessariosReserva(bruto, fid.valorRealPorPonto, fid.pontosPorRealResgate);
}
function podeUsarPontosFidelidade(){
var pages=window.BDMChatPages||{};
if(!pages.isLogged||!pages.isLogged()) return false;
if(fid.saldo===null||!fid.programaAtivo) return false;
if(!selPerm||selPerm.disabled||selPerm.value==="") return false;
if(!selChe||selChe.disabled||selChe.value==="") return false;
var bruto=getValorBrutoPeriodoAtual();
if(bruto <=0) return false;
var need=getPontosNecessariosAtual();
if(need <=0) return false;
return Math.floor(Number(fid.saldo)) >=need;
}
function syncCupomWrapVisibility(){
if(!cupomWrap) return;
if(fid.usarPontos||fid.ocultarCampoCupom) cupomWrap.setAttribute("hidden", "hidden");
else cupomWrap.removeAttribute("hidden");
}
function syncFidelidadeUi(){
if(!fidelidadeBox) return;
if(podeUsarPontosFidelidade()){
fidelidadeBox.removeAttribute("hidden");
var need=getPontosNecessariosAtual();
if(fidelidadeHint){
var hintPontos=fid.pontosUtilizados > 0 ? fid.pontosUtilizados:need;
fidelidadeHint.textContent =
"Esta reserva utiliza " + hintPontos + " pontos (seu saldo: " + fid.saldo + ").";
}}else{
fidelidadeBox.setAttribute("hidden", "hidden");
if(fid.usarPontos||fid.cupom||fid.cupomManual||fid.descontoLabel){
limparDescontoFidelidadeState(fid, getValorBrutoPeriodoAtual());
}
if(fidelidadeCheck) fidelidadeCheck.checked=false;
}
if(fidelidadeCheck){
fidelidadeCheck.checked = !!fid.usarPontos;
fidelidadeCheck.disabled = !!fid.aplicandoCupom;
}
syncCupomWrapVisibility();
}
state.syncFidelidadeUi=syncFidelidadeUi;
function limparEstadoCupomEFidelidade(){
cupomApplySeq +=1;
limparDescontoFidelidadeState(fid, getValorBrutoPeriodoAtual());
if(fidelidadeCheck) fidelidadeCheck.checked=false;
if(cupomInp) cupomInp.value="";
syncFidelidadeUi();
}
function buildCupomPayload(codigoCupom){
var pages=window.BDMChatPages||{};
var uid=pages.getUsuarioId ? pages.getUsuarioId():0;
var sel=getSelection();
var valorPeriodo=String((getValorBrutoPeriodoAtual()||0).toFixed(2));
var payload={
id_usuario: uid,
id_suite: idSuite,
cupom: codigoCupom,
valor_periodo: valorPeriodo
};
if(codigoCupom!==CUPOM_FIDELIDADE){
payload.valor=valorPeriodo;
payload.valor_bruto=valorPeriodo;
payload.data_reserva=buildReservaDateTimeFromState(state);
payload.chegada_reserva=String(sel.chegada||"");
payload.periodo_reserva=String(sel.periodoReserva||sel.periodoLabel||"");
}
return payload;
}
function aplicarCupomRequest(codigoCupom){
var url=(ctx.urls.cupomAplicar||"").trim();
if(!url) return Promise.reject(new Error("Cupom indisponível."));
var payload=buildCupomPayload(codigoCupom);
var pagesCupom=window.BDMChatPages||{};
if(pagesCupom.fetchWidgetJsonWithStatus){
return pagesCupom.fetchWidgetJsonWithStatus(ctx, "cupomAplicar", payload, url);
}
return fetch(url, {
method: "POST",
credentials: "same-origin",
headers: pagesCupom.mergeWidgetAuthHeaders({ "Content-Type": "application/json" }),
body: JSON.stringify(payload)
}).then(function (r){
return r.json()
.then(function (j){
return { status: r.status, json: j };})
.catch(function (){
return { status: r.status, json: { erro: "Resposta inválida do servidor." }};});
});
}
function removerCupomFidelidade(){
limparEstadoCupomEFidelidade();
refresh();
}
function aplicarCupomFidelidade(){
if(!podeUsarPontosFidelidade()){
toast(ctx, "Saldo de pontos insuficiente para esta reserva.");
return Promise.resolve(false);
}
var seq=++cupomApplySeq;
fid.aplicandoCupom=true;
syncFidelidadeUi();
return aplicarCupomRequest(CUPOM_FIDELIDADE)
.then(function (res){
if(seq!==cupomApplySeq) return false;
var data=res&&res.json ? res.json:{};
if(res.status < 200||res.status >=300){
throw new Error(String((data&&data.erro)||"Não foi possível aplicar os pontos."));
}
var bruto=getValorBrutoPeriodoAtual();
var parsed=parseCupomAplicarResponse(data, bruto);
if(!parsed||parsed.erro){
throw new Error(parsed&&parsed.erro ? parsed.erro:"Cupom de fidelidade inválido.");
}
fid.usarPontos=true;
fid.cupom=CUPOM_FIDELIDADE;
fid.cupomManual="";
applyCupomParsedToFidelidade(fid, parsed, bruto);
if(cupomInp) cupomInp.value="";
refresh();
return true;
})
.catch(function (err){
if(seq!==cupomApplySeq) return false;
limparDescontoFidelidadeState(fid, getValorBrutoPeriodoAtual());
if(fidelidadeCheck) fidelidadeCheck.checked=false;
toast(ctx, err&&err.message ? err.message:"Não foi possível usar os pontos.");
refresh();
return false;
})
.finally(function (){
fid.aplicandoCupom=false;
syncFidelidadeUi();
});
}
function aplicarCupomManual(){
if(fid.usarPontos){
toast(ctx, "Remova os pontos de fidelidade para usar outro cupom.");
return;
}
var c=cupomInp ? cupomInp.value.trim().toUpperCase():"";
if(!c){
toast(ctx, "Informe um cupom.");
return;
}
if(c===CUPOM_FIDELIDADE){
toast(ctx, "Use a opção acima para pagar com pontos de fidelidade.");
return;
}
var sel=getSelection();
if(sel.blocked||!sel.periodoLabel||!sel.chegada){
toast(ctx, "Selecione permanência e horário de chegada.");
return;
}
var seqManual=++cupomApplySeq;
fid.aplicandoCupom=true;
if(applyBtn) applyBtn.disabled=true;
aplicarCupomRequest(c)
.then(function (res){
if(seqManual!==cupomApplySeq) return;
var data=res&&res.json ? res.json:{};
if(res.status < 200||res.status >=300){
throw new Error(String((data&&data.erro)||"Cupom inválido."));
}
var bruto=getValorBrutoPeriodoAtual();
var parsed=parseCupomAplicarResponse(data, bruto);
if(!parsed||parsed.erro){
throw new Error(parsed&&parsed.erro ? parsed.erro:"Cupom inválido.");
}
fid.cupomManual=c;
fid.cupom=c;
fid.usarPontos=false;
applyCupomParsedToFidelidade(fid, parsed, bruto);
refresh();
toast(ctx, "Cupom aplicado.");
})
.catch(function (err){
if(seqManual===cupomApplySeq){
limparDescontoFidelidadeState(fid, getValorBrutoPeriodoAtual());
}
toast(ctx, err&&err.message ? err.message:"Não foi possível aplicar o cupom.");
refresh();
})
.finally(function (){
fid.aplicandoCupom=false;
if(applyBtn) applyBtn.disabled=false;
syncFidelidadeUi();
});
}
function onUsarPontosFidelidadeChange(checked){
if(!checked){
removerCupomFidelidade();
return;
}
if(!podeUsarPontosFidelidade()){
if(fidelidadeCheck) fidelidadeCheck.checked=false;
toast(ctx, "Saldo de pontos insuficiente para esta reserva.");
return;
}
aplicarCupomFidelidade();
}
carregarSaldoFidelidade(ctx, fid).then(function (){
syncFidelidadeUi();
refresh();
});
function isDateSelectionBlocked(){
return (
isDateBeforeMin(state.selY, state.selM, state.selD, minDate) ||
(isDisp0Combined(merged, base)&&isRealToday(state.selY, state.selM, state.selD))
);
}
function isPrecosEmptyForDay(){
return !lastPrecosArr.length;
}
function currentPermOption(){
var opt=selPerm.options[selPerm.selectedIndex];
if(!opt||opt.value===""||!opt._bdm){
return { label: "", valor: NaN, raw: null };}
return opt._bdm;
}
function currentChegada(){
var opt=selChe.options[selChe.selectedIndex];
if(!opt||opt.value==="") return "";
return opt._bdmH||opt.textContent||"";
}
function getSelection(){
var p=currentPermOption();
var c=currentChegada();
var v=p&&isFinite(p.valor) ? p.valor:NaN;
return {
blocked: isSelectionBlocked(),
periodoLabel: p&&p.label ? p.label:"",
periodoReserva: p&&p.periodoApi ? String(p.periodoApi):(p&&p.label ? normalizePeriodoReserva(p.label):""),
chegada: c||"",
valor: isFinite(v) ? v:0
};}
function isSelectionBlocked(){
return isDateSelectionBlocked()||isPrecosEmptyForDay();
}
function refresh(){
var blocked=isSelectionBlocked();
if(blocked){
updateResumo(ctx, node, state, null, blocked&&isPrecosEmptyForDay()&&!isDateSelectionBlocked() ? "Indisponível":"--");
syncTermsButton();
return;
}
var p=currentPermOption();
var c=currentChegada();
var v=p.valor;
if(!isFinite(v)) v=fb;
updateResumo(
ctx,
node,
state,
p&&p.label ? p:null,
c||(selChe&&selChe.value==="" ? "":c)
);
syncTermsButton();
}
function isReservaFormReady(){
if(isSelectionBlocked()) return false;
if(!termos||!termos.checked) return false;
if(suiteInterativa&&state.interacaoChecked&&!state.termosInteracaoChecked) return false;
if(isDateBeforeMin(state.selY, state.selM, state.selD, minDate)) return false;
if(isDisp0Combined(merged, base)&&isRealToday(state.selY, state.selM, state.selD)) return false;
if(!selPerm||selPerm.disabled||selPerm.value==="") return false;
if(!selChe||selChe.disabled||selChe.value==="") return false;
var p=currentPermOption();
if(!p||!p.label) return false;
var c=currentChegada();
if(!c||!String(c).trim()) return false;
return true;
}
function syncTermsButton(){
setReservarEnabled(resBtn, isReservaFormReady());
}
function applySelectsState(arr){
lastPrecosArr=arr&&arr.length ? arr.slice():[];
var dateBlocked=isDateSelectionBlocked();
var precosEmpty = !lastPrecosArr.length;
state.precosEmptyForDay = !dateBlocked&&precosEmpty;
if(dateBlocked||precosEmpty){
lockBookingSelects(selPerm, selChe);
}else{
selPerm.disabled=false;
selChe.disabled=false;
fillPermanencia(selPerm, lastPrecosArr, fb);
fillChegada(selChe, state, state.intervalosFechamento);
}
refresh();
syncTermsButton();
}
applySelectsState(lastPrecosArr);
selPerm.addEventListener("change", function (){
limparEstadoCupomEFidelidade();
refresh();
});
selChe.addEventListener("change", function (){
limparEstadoCupomEFidelidade();
refresh();
});
if(termos){
termos.addEventListener("change", function (){
if(!termos.checked){
syncTermsButton();
return;
}
termos.checked=false;
syncTermsButton();
openTermosModalComFallback(node, function (){
termos.checked=true;
syncTermsButton();
});
});
}
if(interacaoCheck){
interacaoCheck.addEventListener("change", function (){
state.interacaoChecked = !!interacaoCheck.checked;
if(!state.interacaoChecked){
state.termosInteracaoChecked=false;
if(termosInteracaoCheck) termosInteracaoCheck.checked=false;
}
syncInteracaoUi();
});
}
if(termosInteracaoCheck){
termosInteracaoCheck.addEventListener("change", function (){
if(!state.interacaoChecked){
termosInteracaoCheck.checked=false;
state.termosInteracaoChecked=false;
syncTermsButton();
return;
}
if(!termosInteracaoCheck.checked){
state.termosInteracaoChecked=false;
syncTermsButton();
return;
}
termosInteracaoCheck.checked=false;
state.termosInteracaoChecked=false;
syncTermsButton();
openTermosInterativaModalComFallback(node, function (){
state.termosInteracaoChecked=true;
if(termosInteracaoCheck) termosInteracaoCheck.checked=true;
syncTermsButton();
});
});
}
syncInteracaoUi();
if(applyBtn){
applyBtn.addEventListener("click", function (e){
e.preventDefault();
e.stopPropagation();
aplicarCupomManual();
});
}
if(fidelidadeCheck){
fidelidadeCheck.addEventListener("change", function (){
onUsarPontosFidelidadeChange(!!fidelidadeCheck.checked);
});
}
if(termLink){
termLink.addEventListener("click", function (e){
e.preventDefault();
e.stopPropagation();
openTermosModalComFallback(node, function (){
if(termos) termos.checked=true;
syncTermsButton();
});
});
}
return {
refresh: refresh,
termos: termos,
getSelection: getSelection,
getPontosNecessariosAtual: getPontosNecessariosAtual,
podeUsarPontosFidelidade: podeUsarPontosFidelidade,
limparCupomEFidelidade: limparEstadoCupomEFidelidade,
refillChegada: function (){
applySelectsState(lastPrecosArr);
},
refillPermanencia: function (arr){
limparEstadoCupomEFidelidade();
lastPrecosArr=arr&&arr.length ? arr.slice():[];
applySelectsState(lastPrecosArr);
},
syncTermsButton: syncTermsButton
};}
window.BDMChatPages.suiteDetalhes=function (ctx){
ctx.state.currentView="suiteDetalhes";
var base=ctx.state.selectedSuite||{};
var idSuite=parseInt(
String(base.id_suite!=null&&base.id_suite!=="" ? base.id_suite:base.id||""),
10
);
if(!idSuite){
ctx.body.textContent="Suíte inválida.";
return;
}
var detUrl=(ctx.urls.suiteDetalhes||"").trim();
var preUrl=(ctx.urls.suitePrecos||"").trim();
if(!detUrl||!preUrl){
ctx.body.textContent="Rotas de detalhes não configuradas.";
return;
}
var pagesLoad=window.BDMChatPages||{};
function fetchDetalhes(){
if(pagesLoad.fetchWidgetJson){
return pagesLoad.fetchWidgetJson(ctx, "detalhes", { id_suite: idSuite }, detUrl);
}
return fetch(detUrl, {
method: "POST",
credentials: "same-origin",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ id_suite: idSuite })
}).then(function (r){ return r.json(); });
}
function fetchPrecos(state){
var fields={ id_suite: idSuite, selectDate: selectDateForPrecosApi(state) };
if(pagesLoad.fetchWidgetJson){
return pagesLoad.fetchWidgetJson(ctx, "precos", fields, preUrl);
}
return fetch(preUrl, {
method: "POST",
credentials: "same-origin",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(fields)
}).then(function (r){ return r.json(); });
}
function fetchTermos(){
return fetchPainelTermosJquery();
}
function fetchIntervalosFechamento(){
var url=(ctx.urls.intervalosFechamento||"").trim();
if(!url) return Promise.resolve([]);
var loadIntervalos=pagesLoad.fetchWidgetJson
? pagesLoad.fetchWidgetJson(ctx, "intervalos", {}, url)
: fetch(url, {
method: "POST",
credentials: "same-origin",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({})
}).then(function (r){ return r.json(); });
return loadIntervalos
.then(function (j){ return normalizeIntervalosList(j); })
.catch(function (){ return []; });
}
var initialMinDate=getInitialPrecosMinDate(base);
var initialPrecosState={
selY: initialMinDate.getFullYear(),
selM: initialMinDate.getMonth(),
selD: initialMinDate.getDate()
};
Promise.all([
ctx.loadTemplate("suite-detalhes"),
fetchDetalhes(),
fetchPrecos(initialPrecosState),
fetchTermos(),
fetchIntervalosFechamento()
])
.then(function (arr){
var html=arr[0];
var detJson=unwrapDetalhesJson(arr[1]);
var preJson=normalizePrecosApiResponse(arr[2]);
var termJson=arr[3];
var intervalosJson=arr[4];
if(detJson&&detJson.erro){
ctx.body.textContent=String(detJson.erro);
return;
}
var suiteApi=normalizeDetalhesPayload(detJson);
var merged=buildMergedSuite(base, suiteApi, detJson);
var minDate=getCalendarMinDateForSuite(merged, base);
var precosMismatch =
!sameCalendarDay(minDate, initialMinDate)||(preJson&&preJson.erro&&!isPrecosUnavailableErroMessage(preJson.erro));
if(precosMismatch){
return fetchPrecos({
selY: minDate.getFullYear(),
selM: minDate.getMonth(),
selD: minDate.getDate()
}).then(function (pre2){
return {
html: html,
detJson: detJson,
preJson: normalizePrecosApiResponse(pre2),
merged: merged,
suiteApi: suiteApi,
minDate: minDate,
termJson: termJson,
intervalosFechamento: intervalosJson
};});
}
return {
html: html,
detJson: detJson,
preJson: preJson,
merged: merged,
suiteApi: suiteApi,
minDate: minDate,
termJson: termJson,
intervalosFechamento: intervalosJson
};})
.then(function (pack){
if(!pack) return;
var html=pack.html;
var preJson=pack.preJson;
var merged=pack.merged;
var minDate=pack.minDate;
if(preJson&&preJson.erro){
ctx.body.textContent=String(preJson.erro);
return;
}
var termosPainel=extractTermosFromRestJson(pack.termJson);
if(termosPainel){
merged.termos=termosPainel;
}
var texto=ctx.htmlToText(merged.texto||"");
var imgItems=imagesFromDetail(merged);
if(!imgItems.length){
var one=ctx.firstImageFromSuite(merged);
if(one) imgItems=[{ image: one, thumb: one }];
}
var precosArr=normalizePrecosList(preJson);
var fallbackValor=parseMoneyAny(merged.apartir||merged.preco||merged.valor);
if(!isFinite(fallbackValor)) fallbackValor=0;
var intervalosFechamento=normalizeIntervalosList(pack.intervalosFechamento);
var state={
calYear: minDate.getFullYear(),
calMonth: minDate.getMonth(),
selY: minDate.getFullYear(),
selM: minDate.getMonth(),
selD: minDate.getDate(),
fallbackValor: fallbackValor,
intervalosFechamento: intervalosFechamento,
precosEmptyForDay: isPrecosListEmpty(preJson),
interacaoChecked: false,
termosInteracaoChecked: false,
suiteInterativa: isSuiteInterativaFlag(merged, base, pack.detJson)
};
var node=ctx.htmlToNode(html);
var gSlot=node.querySelector('[data-slot="gallery"]');
var descSlot=node.querySelector('[data-slot="descricao"]');
var calSlot=node.querySelector('[data-slot="calendar"]');
var bookingRef={ booking: null };
if(gSlot) renderGallery(ctx, gSlot, imgItems);
if(descSlot) descSlot.textContent=texto||"—";
function onDayChanged(){
if(bookingRef.booking&&bookingRef.booking.limparCupomEFidelidade){
bookingRef.booking.limparCupomEFidelidade();
}
syncBookingUnavailableUi(node, merged, base, state, minDate, bookingRef.booking);
fetchPrecos(state).then(function (pj){
pj=normalizePrecosApiResponse(pj);
if(pj&&pj.erro){
ctx.body.textContent=String(pj.erro);
return;
}
var arr=normalizePrecosList(pj);
state.precosEmptyForDay=isPrecosListEmpty(pj);
if(bookingRef.booking&&bookingRef.booking.refillPermanencia){
bookingRef.booking.refillPermanencia(arr);
}
syncBookingUnavailableUi(node, merged, base, state, minDate, bookingRef.booking);
}).catch(function (){
state.precosEmptyForDay=true;
if(bookingRef.booking&&bookingRef.booking.refillPermanencia){
bookingRef.booking.refillPermanencia([]);
}
syncBookingUnavailableUi(node, merged, base, state, minDate, bookingRef.booking);
});
}
if(calSlot){
renderCalendar(ctx, calSlot, state, minDate, onDayChanged);
}
var booking=bindBooking(ctx, node, state, preJson, precosArr, minDate, merged, base, pack.detJson, intervalosFechamento, idSuite);
bookingRef.booking=booking;
syncBookingUnavailableUi(node, merged, base, state, minDate, booking);
ctx.wireActions(node);
var resBtn=node.querySelector("[data-suite-reservar]");
if(resBtn){
resBtn.addEventListener("click", function (e){
e.stopPropagation();
if(isDisp0Combined(merged, base)&&isRealToday(state.selY, state.selM, state.selD)){
toast(ctx, "Esta suíte não está disponível para hoje. Escolha amanhã ou outra data.");
return;
}
if(isDateBeforeMin(state.selY, state.selM, state.selD, minDate)){
toast(ctx, "Esta suíte não está disponível na data selecionada. Escolha outro dia.");
return;
}
if(state.precosEmptyForDay){
toast(ctx, "Indisponível para o dia selecionado.");
return;
}
if(booking.termos&&!booking.termos.checked){
toast(ctx, "Aceite os termos de uso para continuar.");
return;
}
if(state.suiteInterativa&&state.interacaoChecked&&!state.termosInteracaoChecked){
toast(ctx, "Aceite os termos de uso da interação.");
return;
}
var selection=booking.getSelection ? booking.getSelection():null;
if(!selection||selection.blocked){
toast(ctx, "Selecione uma data disponível para reservar.");
return;
}
if(!selection.periodoLabel||!String(selection.periodoLabel).trim()){
toast(ctx, "Selecione a permanência.");
return;
}
if(!selection.chegada||!String(selection.chegada).trim()){
toast(ctx, "Selecione o horário de chegada.");
return;
}
var pages=window.BDMChatPages||{};
var uid=pages.getUsuarioId ? pages.getUsuarioId():0;
if(!uid){
toast(ctx, "Faça login novamente para concluir a reserva.");
return;
}
var user=pages.getUserRecord ? pages.getUserRecord():null;
var userPayload=user&&user.payload&&typeof user.payload==="object" ? user.payload:{};
var fid=state.fidelidade||{};
var valorBruto =
isFinite(fid.valorBrutoPeriodo)&&fid.valorBrutoPeriodo > 0
? fid.valorBrutoPeriodo
: isFinite(selection.valor)
? selection.valor
: 0;
var valorFinal=valorBruto;
if(fidelidadeCupomAtivo(fid)&&isFinite(fid.valorTotal)){
valorFinal=fid.valorTotal;
}
var pontosEnvio=0;
var cupomEnvio=String(fid.cupom||fid.cupomManual||"").trim();
if(fid.usarPontos){
pontosEnvio=fid.pontosUtilizados > 0
? fid.pontosUtilizados
: booking.getPontosNecessariosAtual
? booking.getPontosNecessariosAtual()
: pontosNecessariosReserva(valorBruto, fid.valorRealPorPonto, fid.pontosPorRealResgate);
if(!pontosEnvio||Math.floor(Number(fid.saldo)) < pontosEnvio){
toast(ctx, "Saldo de pontos insuficiente para esta reserva.");
return;
}
if(!cupomEnvio) cupomEnvio=CUPOM_FIDELIDADE;
}
var interacaoReserva="N";
if(state.suiteInterativa&&state.interacaoChecked===true) interacaoReserva="S";
var reservaPayload={
id_suite: idSuite,
id_usuario: uid,
data_reserva: buildReservaDateTimeFromState(state),
chegada_reserva: String(selection.chegada),
periodo_reserva: String(selection.periodoReserva||selection.periodoLabel||""),
valor_reserva: String(Number(valorFinal).toFixed(2)),
valor_reserva_total: String(Number(valorBruto).toFixed(2)),
interacao_reserva: interacaoReserva,
pontos_fidelidade: fid.usarPontos ? pontosEnvio:0,
cupom: cupomEnvio,
nome: String((user&&user.nome)||userPayload.nome||"").trim(),
email: String((user&&user.email)||userPayload.email||"").trim(),
telefone: String((user&&user.telefone)||userPayload.telefone||"").trim(),
cpf: String((user&&user.cpf)||userPayload.cpf||"").trim()
};
window.BDMChatPages.reservarSuite(ctx, reservaPayload, resBtn);
});
}
ctx.body.innerHTML="";
ctx.body.appendChild(node);
})
.catch(function (){
ctx.body.textContent="Falha ao carregar detalhes da suíte.";
});
};
function extractReservaCode(api){
if(Array.isArray(api)&&api.length&&api[0]&&typeof api[0]==="object"){
return String(api[0].codigo_reserva||api[0].codigo||api[0].codigoPedido||"").trim();
}
if(!api||typeof api!=="object") return "";
if(Array.isArray(api.data)&&api.data.length&&api.data[0]&&typeof api.data[0]==="object"){
return String(api.data[0].codigo_reserva||api.data[0].codigo||api.data[0].codigoPedido||"").trim();
}
return String(api.codigo_reserva||api.codigo||api.codigoPedido||"").trim();
}
function rememberPendingReservaApproval(data){
try {
localStorage.setItem(LS_PENDING_RESERVA, JSON.stringify(data||{}));
} catch (e){}}
function clearPendingReservaApproval(){
try {
localStorage.removeItem(LS_PENDING_RESERVA);
} catch (e){}}
function rememberPendingPayment(data){
try {
localStorage.setItem(LS_PENDING_PAYMENT, JSON.stringify(data||{}));
} catch (e){}}
function clearPendingPayment(){
try {
localStorage.removeItem(LS_PENDING_PAYMENT);
} catch (e){}}
function clearReservaCheckTimer(ctx){
if(!ctx||!ctx.state) return;
if(ctx.state.reservaCheckTimer){
clearTimeout(ctx.state.reservaCheckTimer);
ctx.state.reservaCheckTimer=null;
}}
function extractCheckResult(api){
if(!api||typeof api!=="object") return "";
return String(api.result||api.status||"").trim().toUpperCase();
}
function clearPagamentoCheckTimer(ctx){
if(!ctx||!ctx.state) return;
if(ctx.state.pagamentoCheckTimer){
clearTimeout(ctx.state.pagamentoCheckTimer);
ctx.state.pagamentoCheckTimer=null;
}}
function extractPreferenceId(api){
if(Array.isArray(api)&&api.length&&api[0]&&typeof api[0]==="object"){
return String(api[0].id||api[0].preference_id||"").trim();
}
if(!api||typeof api!=="object") return "";
if(Array.isArray(api.data)&&api.data.length&&api.data[0]&&typeof api.data[0]==="object"){
return String(api.data[0].id||api.data[0].preference_id||"").trim();
}
return String(api.id||api.preference_id||"").trim();
}
function extractPixPayload(api){
if(!api||typeof api!=="object") return null;
var poi=api.point_of_interaction&&typeof api.point_of_interaction==="object"
? api.point_of_interaction
: {};
var tx=poi.transaction_data&&typeof poi.transaction_data==="object"
? poi.transaction_data
: {};
var code=String(tx.qr_code||"").trim();
var base64=String(tx.qr_code_base64||"").trim();
if(!code&&!base64) return null;
return { code: code, base64: base64 };}
function formatDateDisplay(raw){
var s=String(raw==null ? "":raw).trim();
var m=s.match(/^(\d{4})-(\d{2})-(\d{2})/);
if(m) return m[3] + "-" + m[2] + "-" + m[1];
return s;
}
function formatMoneyDisplay(raw){
var n=parseFloat(String(raw==null ? "":raw).replace(",", "."));
if(!isFinite(n)) return "R$ 0,00";
return "R$ " + n.toFixed(2).replace(".", ",");
}
window.BDMChatPages.reservarSuite=function (ctx, payload, btn){
if(ctx&&ctx.state&&ctx.state.reservaSubmitInFlight){
toast(ctx, "Aguarde, estamos processando sua reserva.");
return;
}
clearReservaCheckTimer(ctx);
clearPagamentoCheckTimer(ctx);
clearPendingPayment();
var url=(ctx.urls.reservaSave||"").trim();
if(!url){
toast(ctx, "Rota de reserva não configurada.");
return;
}
if(!payload||typeof payload!=="object"){
toast(ctx, "Dados da reserva inválidos.");
return;
}
if(ctx&&ctx.state) ctx.state.reservaSubmitInFlight=true;
if(btn) btn.disabled=true;
var pages=window.BDMChatPages||{};
var savePromise=pages.saveReserva
? pages.saveReserva(ctx, payload, url)
: fetch(url, {
method: "POST",
credentials: "same-origin",
headers: window.BDMChatPages.mergeWidgetAuthHeaders({ "Content-Type": "application/json" }),
body: JSON.stringify(payload)
}).then(function (r){
return r.json().then(function (j){
return { status: r.status, json: j };});
});
savePromise
.then(function (res){
if(ctx&&ctx.state) ctx.state.reservaSubmitInFlight=false;
if(btn) btn.disabled=false;
var data=res&&res.json ? res.json:{};
if(typeof data==="string"){
data={ erro: data.trim()||"Resposta inválida ao salvar a reserva." };}
var errText="";
if(data&&data.erro!=null&&String(data.erro).trim()) errText=String(data.erro).trim();
else if(data&&data.message!=null&&String(data.message).trim()) errText=String(data.message).trim();
if(res.status < 200||res.status >=300||(data&&data.result==="error")||errText){
toast(ctx, errText||"Não foi possível concluir a reserva.");
return;
}
ctx.state.lastReservaResult={
code: extractReservaCode(data),
payload: payload,
response: data
};
if(ctx.state.lastReservaResult.code){
rememberPendingReservaApproval({
codigo_reserva: ctx.state.lastReservaResult.code,
id_usuario: payload.id_usuario||0,
payload: payload,
date_create: Date.now(),
status: "Pendente"
});
}
ctx.navigate("reservaAguarde");
})
.catch(function (){
if(ctx&&ctx.state) ctx.state.reservaSubmitInFlight=false;
if(btn) btn.disabled=false;
toast(ctx, "Falha ao enviar a reserva.");
});
};
window.BDMChatPages.reservaAguarde=function (ctx){
ctx.state.currentView="reservaAguarde";
clearReservaCheckTimer(ctx);
clearPagamentoCheckTimer(ctx);
var result=ctx.state.lastReservaResult||{};
var code=result&&result.code ? String(result.code):"";
var checkUrl=(ctx.urls.reservaCheck||"").trim();
var bathtub=(window.motelswingChat&&window.motelswingChat.assetBathtub)||"";
ctx.loadTemplate("reserva-aguarde")
.then(function (html){
var node=ctx.htmlToNode(html);
var codeEl=node.querySelector('[data-slot="reserva-codigo"]');
if(codeEl) codeEl.textContent=code||"—";
var img=node.querySelector('[data-slot="reserva-aguarde-img"]');
if(img&&bathtub) img.setAttribute("src", bathtub);
ctx.body.innerHTML="";
ctx.body.appendChild(node);
function runCheck(){
if(ctx.state.currentView!=="reservaAguarde") return;
var pages=window.BDMChatPages||{};
var uid=pages.getUsuarioId ? pages.getUsuarioId():0;
if(!checkUrl||!uid||!code) return;
fetch(checkUrl, {
method: "POST",
credentials: "same-origin",
headers: window.BDMChatPages.mergeWidgetAuthHeaders({ "Content-Type": "application/json" }),
body: JSON.stringify({ id_usuario: uid, codigo_reserva: code })
})
.then(function (r){
return r.json().then(function (j){ return { status: r.status, json: j };});
})
.then(function (res){
if(ctx.state.currentView!=="reservaAguarde") return;
var data=res&&res.json ? res.json:{};
if(res.status < 200||res.status >=300||data.erro){
ctx.state.reservaCheckTimer=setTimeout(runCheck, 5000);
return;
}
var resultStatus=extractCheckResult(data);
if(resultStatus==="WAIT"||resultStatus===""){
ctx.state.reservaCheckTimer=setTimeout(runCheck, 5000);
return;
}
if(resultStatus==="REFUSED"){
clearReservaCheckTimer(ctx);
clearPendingReservaApproval();
clearPendingPayment();
ctx.navigate("reservaRecusada");
return;
}
if(resultStatus==="OK"){
clearReservaCheckTimer(ctx);
clearPendingReservaApproval();
var payload=result&&result.payload&&typeof result.payload==="object" ? result.payload:{};
ctx.state.lastReservaPaymentData={
codigo_reserva: code,
id_usuario: payload.id_usuario||"",
id_suite: payload.id_suite||"",
data_reserva: payload.data_reserva||"",
chegada_reserva: payload.chegada_reserva||"",
periodo_reserva: payload.periodo_reserva||"",
valor_reserva: payload.valor_reserva||"",
nome: payload.nome||"",
email: payload.email||"",
telefone: payload.telefone||"",
cpf: payload.cpf||""
};
rememberPendingPayment({
codigo_reserva: code,
id_usuario: payload.id_usuario||0,
payload: ctx.state.lastReservaPaymentData,
status: "PENDENTE_PAGAMENTO",
date_create: Date.now()
});
ctx.navigate("reservaPagamento");
return;
}
ctx.state.reservaCheckTimer=setTimeout(runCheck, 5000);
})
.catch(function (){
if(ctx.state.currentView!=="reservaAguarde") return;
ctx.state.reservaCheckTimer=setTimeout(runCheck, 5000);
});
}
ctx.state.reservaCheckTimer=setTimeout(runCheck, 5000);
})
.catch(function (){
ctx.body.textContent="Falha ao carregar tela de confirmação.";
});
};
window.BDMChatPages.reservaRecusada=function (ctx){
ctx.state.currentView="reservaRecusada";
clearReservaCheckTimer(ctx);
clearPagamentoCheckTimer(ctx);
clearPendingReservaApproval();
clearPendingPayment();
var sad=(window.motelswingChat&&window.motelswingChat.assetSad)||"";
ctx.loadTemplate("reserva-recusada")
.then(function (html){
var node=ctx.htmlToNode(html);
var img=node.querySelector('[data-slot="reserva-recusada-img"]');
if(img&&sad) img.setAttribute("src", sad);
var btn=node.querySelector('[data-action="backHome"]');
if(btn){
btn.addEventListener("click", function (e){
e.preventDefault();
ctx.navigate("home");
});
}
ctx.body.innerHTML="";
ctx.body.appendChild(node);
})
.catch(function (){
ctx.body.textContent="Falha ao carregar status da reserva.";
});
};
window.BDMChatPages.reservaPagamento=function (ctx){
ctx.state.currentView="reservaPagamento";
clearReservaCheckTimer(ctx);
clearPagamentoCheckTimer(ctx);
clearPendingReservaApproval();
var data=ctx.state.lastReservaPaymentData||{};
var publicKey=(window.motelswingChat&&window.motelswingChat.mpPublicKey)||"";
var preferUrl=(ctx.urls.pagamentoPreference||"").trim();
var submitUrl=(ctx.urls.pagamentoSubmit||"").trim();
var checkUrl=(ctx.urls.pagamentoCheck||"").trim();
ctx.loadTemplate("reserva-pagamento")
.then(function (html){
var node=ctx.htmlToNode(html);
var code=node.querySelector('[data-slot="pagamento-codigo"]');
var dt=node.querySelector('[data-slot="pagamento-data"]');
var chegada=node.querySelector('[data-slot="pagamento-chegada"]');
var periodo=node.querySelector('[data-slot="pagamento-periodo"]');
var valor=node.querySelector('[data-slot="pagamento-valor"]');
if(code) code.textContent=String(data.codigo_reserva||"—");
if(dt) dt.textContent=formatDateDisplay(data.data_reserva||"");
if(chegada) chegada.textContent=String(data.chegada_reserva||"--:--");
if(periodo) periodo.textContent=String(data.periodo_reserva||"--");
if(valor) valor.textContent=formatMoneyDisplay(data.valor_reserva||"");
var note=node.querySelector('[data-slot="pagamento-note"]');
var brickWrap=node.querySelector('[data-slot="pagamento-brick-wrap"]');
var pixWrap=node.querySelector('[data-slot="pagamento-pix"]');
var pixQr=node.querySelector('[data-slot="pagamento-pix-qr"]');
var pixCode=node.querySelector('[data-slot="pagamento-pix-code"]');
var pixCopyBtn=node.querySelector('[data-action="copyPixCode"]');
var pages=window.BDMChatPages||{};
var uid=pages.getUsuarioId ? pages.getUsuarioId():0;
var codigo=String(data.codigo_reserva||"").trim();
function setNote(msg){
if(!note) return;
note.hidden = !msg;
note.textContent=msg||"";
}
function setBrickLoading(on){
if(!brickWrap) return;
brickWrap.classList.toggle("-loading", !!on);
}
function setPixData(pix){
if(!pixWrap||!pixQr||!pixCode) return;
if(!pix||(!pix.code&&!pix.base64)){
pixWrap.hidden=true;
pixCode.value="";
pixQr.removeAttribute("src");
return;
}
if(pix.base64){
pixQr.src="data:image/png;base64," + pix.base64;
}else{
pixQr.removeAttribute("src");
}
pixCode.value=pix.code||"";
pixWrap.hidden=false;
setTimeout(function (){
try {
pixWrap.scrollIntoView({ behavior: "smooth", block: "end" });
if(ctx&&ctx.body){
ctx.body.scrollTop=ctx.body.scrollHeight;
}} catch (e){}}, 120);
}
function runPagamentoCheck(){
if(ctx.state.currentView!=="reservaPagamento") return;
if(!checkUrl||!uid||!codigo) return;
fetch(checkUrl, {
method: "POST",
credentials: "same-origin",
headers: window.BDMChatPages.mergeWidgetAuthHeaders({ "Content-Type": "application/json" }),
body: JSON.stringify({ id_usuario: uid, codigo_reserva: codigo })
})
.then(function (r){ return r.json().then(function (j){ return { status: r.status, json: j };});})
.then(function (res){
if(ctx.state.currentView!=="reservaPagamento") return;
var payload=res&&res.json ? res.json:{};
if(res.status < 200||res.status >=300||payload.erro){
ctx.state.pagamentoCheckTimer=setTimeout(runPagamentoCheck, 30000);
return;
}
if(extractCheckResult(payload)==="OK"){
clearPagamentoCheckTimer(ctx);
clearPendingPayment();
toast(ctx, "Pagamento confirmado.");
ctx.state.selectedReservaRaw=Object.assign({}, data||{}, {
codigo_reserva: codigo,
id_usuario: uid
});
ctx.state.lastReservaPaymentData=ctx.state.selectedReservaRaw;
ctx.navigate("reservaDetalhe");
return;
}
if(extractCheckResult(payload)==="REFUSED"){
clearPagamentoCheckTimer(ctx);
clearPendingPayment();
ctx.navigate("reservaRecusada");
return;
}
ctx.state.pagamentoCheckTimer=setTimeout(runPagamentoCheck, 30000);
})
.catch(function (){
if(ctx.state.currentView!=="reservaPagamento") return;
ctx.state.pagamentoCheckTimer=setTimeout(runPagamentoCheck, 30000);
});
}
ctx.body.innerHTML="";
ctx.body.appendChild(node);
if(pixCopyBtn){
pixCopyBtn.addEventListener("click", function (e){
e.preventDefault();
var txt=pixCode ? String(pixCode.value||"").trim():"";
if(!txt){
setNote("Código Pix ainda não disponível.");
return;
}
if(navigator.clipboard&&navigator.clipboard.writeText){
navigator.clipboard.writeText(txt).then(function (){
toast(ctx, "Código Pix copiado.");
}).catch(function (){
setNote("Não foi possível copiar automaticamente.");
});
}else{
try {
pixCode.focus();
pixCode.select();
document.execCommand ("copy");
toast(ctx, "Código Pix copiado.");
} catch (err){
setNote("Não foi possível copiar automaticamente.");
}}
});
}
if(!publicKey){
setNote("Chave pública do Mercado Pago não configurada.");
return;
}
if(!preferUrl||!submitUrl||!checkUrl){
setNote("Rotas de pagamento não configuradas.");
return;
}
if(!uid||!codigo){
setNote("Dados da reserva inválidos para pagamento.");
return;
}
rememberPendingPayment({
codigo_reserva: codigo,
id_usuario: uid,
payload: data,
status: "PENDENTE_PAGAMENTO",
date_create: Date.now()
});
setBrickLoading(true);
setNote("Carregando opções de pagamento...");
fetch(preferUrl, {
method: "POST",
credentials: "same-origin",
headers: window.BDMChatPages.mergeWidgetAuthHeaders({ "Content-Type": "application/json" }),
body: JSON.stringify({ id_usuario: uid, codigo_reserva: codigo })
})
.then(function (r){ return r.json().then(function (j){ return { status: r.status, json: j };});})
.then(function (res){
var prefPayload=res&&res.json ? res.json:{};
if(res.status < 200||res.status >=300||prefPayload.erro){
setBrickLoading(false);
setNote(String(prefPayload.erro||prefPayload.message||"Não foi possível iniciar pagamento."));
return;
}
var prefId=extractPreferenceId(prefPayload);
if(!prefId){
setBrickLoading(false);
setNote("Preference do pagamento não retornada.");
return;
}
if(typeof window.BDMChatStartPagamento!=="function"){
setBrickLoading(false);
setNote("Módulo de pagamento não carregado.");
return;
}
window.BDMChatStartPagamento({
publicKey: publicKey,
amount: parseFloat(String(data.valor_reserva||"").replace(",", "."))||0,
preferenceId: prefId,
codigoReserva: codigo,
idUsuario: uid,
submitUrl: submitUrl,
onReady: function (){
setBrickLoading(false);
setNote("");
},
onPaymentCreated: function (_paymentId, payload){
var pix=extractPixPayload(payload);
setPixData(pix);
},
onError: function (err){
setBrickLoading(false);
setNote(err&&err.message ? err.message:"Falha ao enviar pagamento.");
}}).then(function (){
ctx.state.pagamentoCheckTimer=setTimeout(runPagamentoCheck, 30000);
}).catch(function (err){
setBrickLoading(false);
setNote(err&&err.message ? err.message:"Falha ao carregar checkout.");
});
})
.catch(function (){
setBrickLoading(false);
setNote("Falha na comunicação com o serviço de pagamento.");
});
})
.catch(function (){
ctx.body.textContent="Falha ao carregar página de pagamento.";
});
};})();