1function escapeHTML(e){if(!e)return"";const t=document.createElement("div");return t.textContent=e,t.innerHTML}let currentFilter=sessionStorage.getItem("homeWidgetFilter")||"all";currentFilter==="events"&&(currentFilter="all");let widgetData={events:[],groups:[],progress:[],offers:[]},handlersSetup=!1,isNavigating=!1;window.initHomeWidgets=async function(){const e=document.getElementById("home-widget-scroll"),t=document.getElementById("home-widget-tabs");isNavigating=!1,window.homeWidgets={loadOffers,refresh:()=>loadAllWidgetData().then(()=>renderWidget())};const o=document.getElementById("home-widget-container");if(!e)return;let r=!1;try{let n=0;for(;!window.premiumManager&&n<10;)await new Promise(a=>setTimeout(a,100)),n++;window.premiumManager?(r=window.premiumManager.hasAnyEntitlement?await window.premiumManager.hasAnyEntitlement():await window.premiumManager.isPremium(),console.log("[HomeWidgets] Has entitlement:",r)):console.warn("[HomeWidgets] Premium manager not available")}catch(n){console.error("[HomeWidgets] Error checking entitlement:",n)}if(!r){t&&t.classList.add("hidden"),o&&o.classList.add("is-compact");const a=(window.Capacitor?.getPlatform?.()||"web")==="ios";e.innerHTML=` 2 <div class="home-widget-premium-gate"> 3 <h3>Premium Widget</h3> 4 <p>Offers, Groups & Rewards progress</p> 5 <button class="upgrade-home-widget-btn">Access</button> 6 </div> 7 `;const d=e.querySelector(".upgrade-home-widget-btn");d&&d.addEventListener("click",()=>{window.premiumManager?window.premiumManager.showUpgradeModal():typeof showScreen=="function"&&showScreen("info")});const l=document.getElementById("home-widget-dots");l&&(l.innerHTML="");return}if(t&&t.classList.remove("hidden"),o&&o.classList.remove("is-compact"),setupTabHandlers(),setupCardClickHandlers(),await loadAllWidgetData(),renderWidget(),widgetData.offers.length===0&&localStorage.getItem("aura_account_code")){const n=async()=>{console.log("[HomeWidgets] Auth ready â retrying offers load"),await loadOffers(),widgetData.offers.length>0&&renderWidget()};window._authReadyPromise&&window._authReadyPromise.then(()=>{setTimeout(n,500)}).catch(()=>{}),window.addEventListener("auth-ready",()=>setTimeout(n,500),{once:!0})}sessionStorage.getItem("homeWidgetsNeedRefresh")==="true"&&(sessionStorage.removeItem("homeWidgetsNeedRefresh"),console.log("[HomeWidgets] Refreshing due to pending updates"),setTimeout(async()=>{await loadAllWidgetData(),renderWidget()},500)),setupScrollIndicator()};async function loadAllWidgetData(){await Promise.all([loadEvents(),loadGroups(),loadJourneyProgress(),loadOffers()])}async function loadEvents(){try{await ensureEventsLoaded();let e=[];
7if(window.EventsUI&&window.EventsUI.events&&Array.isArray(window.EventsUI.events)?e=window.EventsUI.events:window.eventsManager&&typeof window.eventsManager.getEvents=="function"?e=await window.eventsManager.getEvents():window.eventsData&&Array.isArray(window.eventsData)&&(e=window.eventsData),!e||e.length===0){widgetData.events=[];return}const t=new Date;t.setHours(0,0,0,0);const o=[];for(let r=0;r<e.length&&o.length<5;r++){const n=e[r],a=n.start_date||n.startDate||n.date;if(a)try{let d;if(typeof a=="string"&&/^\d{4}-\d{2}-\d{2}$/.test(a)){const[l,g,u]=a.split("-").map(Number);d=new Date(l,g-1,u)}else a?.toDate?d=a.toDate():d=new Date(a);d.setHours(0,0,0,0),!isNaN(d.getTime())&&d>=t&&o.push({id:n.id||n.event_id||n._id||"",name:n.name||n.title||n.event_name||"Event",location:getEventLocation(n),date:d,type:"event"})}catch{}}widgetData.events=o.sort((r,n)=>r.date-n.date).slice(0,5)}catch(e){console.error("Error loading events:",e),widgetData.events=[]}}function getEventLocation(e){if(e.location){if(typeof e.location=="string")return e.location;if(e.location.name)return e.location.name;if(e.location.address)return e.location.address}return e.venue||e.location_name||""}async function loadGroups(){try{const e=localStorage.getItem("auraItinerary")||"[]",t=JSON.parse(e);Array.isArray(t)&&t.length>0?widgetData.groups=t.slice(0,5).map(o=>({id:o.id||o.code||"",name:o.name||o.title||"Activity",businessName:o.company||o.businessName||o.business_name||o.from||o.organizer||"",date:o.date||"",type:"group"})):widgetData.groups=[]}catch(e){console.error("Error loading groups:",e),widgetData.groups=[]}}async function loadJourneyProgress(){try{const e=window.auraFirebase?.auth?.currentUser;if(!e){widgetData.progress=[];return}const{db:t,doc:o,getDoc:r,collection:n,getDocs:a}=await getFirebaseModules();if(!t){widgetData.progress=[];return}const d=o(t,`users/${e.uid}`),l=await r(d),g=l.exists()?l.data().unlocked_nodes||[]:[];if(g.length===0){widgetData.progress=[];return}let u;const p=typeof window<"u"&&window.MonolociJourneyCache;if(p){const i=await p.getGraph();u=i.journeys.map(s=>({id:s.id,data:s.data,nodeIds:(i.nodesByJourney.get(s.id)||[]).map(w=>w.id)}))}else{const i=await a(n(t,"codexes"));u=[];for(const s of i.docs){const w=await a(n(t,`codexes/${s.id}/nodes`));u.push({id:s.id,data:s.data(),nodeIds:w.docs.map(f=>f.id)})}}const E=[];let y=[];try{y=(JSON.parse(localStorage.getItem("aura_gated_journey_types")||"[]")||[]).map(i=>String(i).toLowerCase())}catch{y=[]}const S=i=>{try{const w=(Array.isArray(i.journey_types)&&i.journey_types.length?i.journey_types:Array.isArray(i.types)&&i.types.length?i.types:i.journey_type?[i.journey_type]:[]).map(c=>String(c).toLowerCase());if(w.includes("premium")||i.access_model==="premium"||i.access_model==="premium_or_code")return!1;let f=w.find(c=>y.includes(c))||null;if(!f&&i.access_model==="purchase_code"&&(f=w[0]||"__no_unlock__"),!f)return!1;let m=[];try{m=JSON.parse(localStorage.getItem("aura_journey_codes")||"[]")||[]}catch{m=[]}const h=Date.now(),b=7*24*60*60*1e3;return!m.some(c=>!c||!c.code||c.invalidAt&&h>c.invalidAt+b||typeof c.accessDurationDays=="number"&&c.redeemedAt&&h>c.redeemedAt+c.accessDurationDays*24*60*60*1e3?!1:(c.journeyTypes||[]).some(x=>String(x).toLowerCase()===f))}catch{return!1}},v=new Set(g);for(const i of u){const s=i.data;if((s.type||s.journey_type||"").toLowerCase()==="mystery"||S(s))continue;const f=i.nodeIds,m=f.length;if(m===0)continue;const h=f.reduce((b,c)=>v.has(c)?b+1:b,0);h>0&&E.push({id:i.id,name:s.title||s.name||"Journey",unlockedCount:h,totalNodes:m,percentage:Math.round(h/m*100),type:"progress"})}widgetData.progress=E.sort((i,s)=>s.percentage-i.percentage).slice(0,5)}catch(e){console.error("Error loading journey progress:",e),widgetData.progress=[]}}async function getFirebaseModules(){try{return window.auraFirebase&&window.auraFirebase.db?{db:window.auraFirebase.db,doc:window.auraFirebase.doc,getDoc:window.auraFirebase.getDoc,collection:window.auraFirebase.collection,getDocs:window.auraFirebase.getDocs,query:window.auraFirebase.query,where:window.auraFirebase.where}:{db:null}}catch{return{db:null}}}function toJSDate(e){if(!e)return null;if(e instanceof Date)return e;if(typeof e.toDate=="function")return e.toDate();const t=e._seconds??e.seconds;return typeof t=="number"?new Date(t*1e3):typeof e=="string"||typeof e=="number"?new Date(e):null}async function waitForAuth(e=15e3){if(window._authReadyPromise)try{const t=await Promise.race([window._authReadyPromise,new Promise((o,r)=>setTimeout(()=>r(new Error("timeout")),e))]);return console.log("[HomeWidgets] Auth ready via promise, uid:",t?.uid),!0}catch(t){return console.warn("[HomeWidgets] Auth promise timed out or failed:",t.message),!1}return new Promise(t=>{if(window.auraFirebase?.auth?.currentUser){t(!0);return}let o=!1;const r=setTimeout(()=>{o||(o=!0,console.warn("[HomeWidgets] Auth wait timed out after",e,"ms"),t(!1))},e);window.addEventListener("auth-ready",()=>{o||(o=!0,clearTimeout(r),console.log("[HomeWidgets] Auth ready event received"),t(!0))},{once:!0})})}let _homeWidgetsLoggedNoAccount=!1;async function loadOffers(){try{(window.debugLog||console.log)("[HomeWidgets] Loading active offers...");const e=localStorage.getItem("aura_account_code");if(!e){_homeWidgetsLoggedNoAccount||((window.debugLog||console.log)("[HomeWidgets] No account code - no offers to load"),_homeWidgetsLoggedNoAccount=!0),widgetData.offers=[];return}console.log("[HomeWidgets] Loading offers for account:",e.substring(0,4)+"****");const t=await getFirebaseModules();if(!t.db){console.log("[HomeWidgets] Firebase not available"),widgetData.offers=[];return}if(!await waitForAuth()){console.warn("[HomeWidgets] Auth not ready â skipping offers query to avoid permission-denied"),widgetData.offers=[];return}if(!!(window.Capacitor?.isNativePlatform?.()&&window.Capacitor?.getPlatform?.()==="ios")&&!window.auraFirebase?.auth?.currentUser){console.warn("[HomeWidgets] iOS native: no authenticated user in native SDK â skipping offers query"),widgetData.offers=[];return}const{db:n,collection:a,query:d,where:l,getDocs:g}=t,u=d(a(n,`users/${e}/activated_offers`),l("status","==","active")),p=await g(u),E=p.size??p.docs?.length??0;console.log("[HomeWidgets] Found",E,"offers in Firebase");const y=p.docs.map(i=>{const s=i.data();return{id:i.id,...s,activated_at:toJSDate(s.activated_at),expires_at:toJSDate(s.expires_at)}}),S=new Date,v=y.filter(i=>{const s=i.expires_at>S&&i.status==="active"&&!i.redeemed;return s||console.log("[HomeWidgets] Filtering out offer:",i.title,"expired:",i.expires_at<=S,"redeemed:",i.redeemed),s});v.sort((i,s)=>i.expires_at-s.expires_at),widgetData.offers=v,console.log("[HomeWidgets] Loaded",v.length,"active offers")}catch(e){console.error("[HomeWidgets] Error loading offers:",e),widgetData.offers=[]}}function renderWidget(){const e=document.getElementById("home-widget-scroll");
7if(!e)return;let t=[];currentFilter==="all"?t=[...widgetData.offers.map(r=>({...r,category:"offers"})),...widgetData.groups.map(r=>({...r,category:"groups"})),...widgetData.progress.map(r=>({...r,category:"progress"}))]:currentFilter==="offers"?t=widgetData.offers.map(r=>({...r,category:"offers"})):currentFilter==="groups"?t=widgetData.groups.map(r=>({...r,category:"groups"})):currentFilter==="progress"&&(t=widgetData.progress.map(r=>({...r,category:"progress"})));const o=document.getElementById("home-widget-container");if(t.length===0){e.innerHTML=renderEmptyState(),o&&o.classList.add("is-compact"),updateScrollDots(0),stopOfferCountdowns();return}o&&o.classList.remove("is-compact"),e.innerHTML=t.map(r=>renderCard(r)).join(""),updateScrollDots(t.length),startOfferCountdowns()}function renderCard(e){return e.category==="events"?renderEventCard(e):e.category==="offers"?renderOfferCard(e):e.category==="groups"?renderGroupCard(e):e.category==="progress"?renderProgressCard(e):""}function renderEventCard(e){const t=e.date?formatDate(e.date):"";return` 8 <div class="home-widget-card event-card" data-type="event" data-id="${escapeHTML(String(e.id))}"> 9 <div class="home-widget-card-badge"> 10 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> 11 <rect x="3" y="4" width="18" height="18" rx="2" ry="2"/> 12 <line x1="16" y1="2" x2="16" y2="6"/> 13 <line x1="8" y1="2" x2="8" y2="6"/> 14 <line x1="3" y1="10" x2="21" y2="10"/> 15 </svg> 16 </div> 17 <div class="home-widget-card-title">${escapeHTML(e.name)}</div> 18 <div class="home-widget-card-subtitle">${escapeHTML(e.location)}</div> 19 <div class="home-widget-card-meta">${escapeHTML(t)}</div> 20 </div> 21 `}function renderGroupCard(e){const t=e.businessName||"",o=e.date||"";return` 22 <div class="home-widget-card group-card" data-type="group" data-id="${escapeHTML(String(e.id))}"> 23 <div class="home-widget-card-badge"> 24 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> 25 <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/> 26 <circle cx="9" cy="7" r="4"/> 27 <path d="M23 21v-2a4 4 0 0 0-3-3.87"/> 28 <path d="M16 3.13a4 4 0 0 1 0 7.75"/> 29 </svg> 30 </div> 31 <div class="home-widget-card-title">${escapeHTML(e.name)}</div> 32 <div class="home-widget-card-subtitle">${escapeHTML(t)}</div> 33 <div class="home-widget-card-meta">${escapeHTML(o)}</div> 34 </div> 35 `}function renderProgressCard(e){return` 36 <div class="home-widget-card progress-card" data-type="progress" data-id="${escapeHTML(String(e.id))}"> 37 <div class="home-widget-card-badge"> 38 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> 39 <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/> 40 <polyline points="22 4 12 14.01 9 11.01"/> 41 </svg> 42 </div> 43 <div class="home-widget-card-title">${escapeHTML(e.name)}</div> 44 <div class="home-widget-card-subtitle">Theme Journey</div> 45 <div class="home-widget-progress-bar"> 46 <div class="home-widget-progress-fill" style="width: ${e.percentage}%"></div> 47 </div> 48 <div class="home-widget-progress-text">${e.unlockedCount}/${e.totalNodes} unlocked</div> 49 </div> 50 `}function offerTimeLeft(e){const t=e-Date.now();if(t<=0)return{text:"Expired",expiringSoon:!0,expired:!0};const o=Math.floor(t/(1e3*60)),r=Math.floor(o/60),n=o%60;return{text:r>0?`${r}h ${n}m left`:n>0?`${n}m left`:"Expiring soon",expiringSoon:r<1,expired:!1}}function renderOfferCard(e){const t=new Date(e.expires_at).getTime(),{text:o,expiringSoon:r}=offerTimeLeft(t);return` 51 <div class="home-widget-card offer-card ${r?"expiring-soon":""}" data-type="offer" data-id="${escapeHTML(String(e.id))}"> 52 <div class="home-widget-card-badge offer-badge"> 53 ð 54 </div> 55 <div class="home-widget-card-title">${escapeHTML(e.title)}</div> 56 <div class="home-widget-card-subtitle">${escapeHTML(e.venue_name)}</div> 57 <div class="home-widget-card-meta">
58 <span class="offer-coupon-code">${escapeHTML(e.coupon_code)}</span> 59 <span class="offer-time-remaining ${r?"warning":""}" data-expires="${t}">${escapeHTML(o)}</span> 60 </div> 61 </div> 62 `}let offerTickTimer=null;function tickOfferCountdowns(){const e=document.querySelectorAll(".offer-time-remaining[data-expires]");if(!e.length){stopOfferCountdowns();return}if(!(typeof window.__monolociActiveScreen=="string"?window.__monolociActiveScreen==="home":!0)){stopOfferCountdowns();return}let o=!1;e.forEach(n=>{const a=Number(n.getAttribute("data-expires"));if(!isFinite(a))return;const{text:d,expiringSoon:l,expired:g}=offerTimeLeft(a);n.textContent!==d&&(n.textContent=d),n.classList.toggle("warning",l);const u=n.closest(".offer-card");u&&(u.classList.toggle("expiring-soon",l),u.classList.toggle("offer-expired",g)),l&&(o=!0)});const r=o?1e3:3e4;offerTickTimer&&offerTickTimer.period!==r&&(clearInterval(offerTickTimer.id),offerTickTimer={id:setInterval(tickOfferCountdowns,r),period:r})}function startOfferCountdowns(){stopOfferCountdowns(),document.querySelector(".offer-time-remaining[data-expires]")&&(offerTickTimer={id:setInterval(tickOfferCountdowns,1e3),period:1e3},tickOfferCountdowns())}function stopOfferCountdowns(){offerTickTimer&&(clearInterval(offerTickTimer.id),offerTickTimer=null)}typeof window<"u"&&window.addEventListener("monoloci:screen-changed",e=>{e&&e.detail&&e.detail.screen==="home"?startOfferCountdowns():stopOfferCountdowns()});function renderEmptyState(){let e="No content available",t="";return currentFilter==="offers"?(e="No active offers",t="Activate offers from venue profiles"):currentFilter==="groups"?(e="No activity groups",t="Enter code in the My Activ tab"):currentFilter==="progress"?(e="No journey progress yet",t="Start unlocking in Rewards Journeys"):(e="No content to show yet",t="Activated Offers, My Activ & Rewards Progress"),` 63 <div class="home-widget-empty"> 64 <p>${e}</p> 65 <p class="subtitle">${t}</p> 66 </div> 67 `}function formatDate(e){if(!e)return"";try{const t={month:"short",day:"numeric"};return e.toLocaleDateString("en-AU",t)}catch{return""}}function setupTabHandlers(){const e=document.querySelectorAll(".home-widget-tab");e.forEach(t=>{t.getAttribute("data-category")===currentFilter?t.classList.add("active"):t.classList.remove("active")}),e.forEach(t=>{t.addEventListener("click",()=>{e.forEach(o=>o.classList.remove("active")),t.classList.add("active"),currentFilter=t.getAttribute("data-category"),sessionStorage.setItem("homeWidgetFilter",currentFilter),renderWidget()})})}function setupCardClickHandlers(){if(handlersSetup)return;const e=document.getElementById("home-widget-scroll");e&&(e.addEventListener("click",t=>{const o=t.target.closest(".home-widget-card");if(!o)return;const r=o.getAttribute("data-type"),n=o.getAttribute("data-id");r==="event"&&n?navigateToEvent(n):r==="offer"&&n?showOfferRedemption(n):r==="group"&&n?navigateToGroup(n):r==="progress"&&n&&navigateToJourney(n)}),handlersSetup=!0)}function navigateToEvent(e){try{if(isNavigating)return;isNavigating=!0,console.log("Navigating to event:",e),typeof window.showScreen=="function"?window.showScreen("group"):typeof showScreen=="function"&&showScreen("group"),setTimeout(()=>{const t=document.getElementById("tab-events");t&&t.click(),setTimeout(()=>{try{const r=(window.EventsUI?.events||window.eventsData||[]).find(n=>String(n.id)===String(e)||String(n.event_id)===String(e));r&&window.EventsUI&&typeof window.EventsUI.showEventDetail=="function"&&window.EventsUI.showEventDetail(r)}finally{isNavigating=!1}},300)},200)}catch(t){console.error("Error navigating to event:",t),isNavigating=!1}}function navigateToGroup(e){try{if(isNavigating)return;if(isNavigating=!0,console.log("Navigating to group:",e),typeof window.openActivityChat=="function"&&window.openActivityChat(e)){isNavigating=!1;return}typeof window.showScreen=="function"?window.showScreen("group"):typeof showScreen=="function"&&showScreen("group"),setTimeout(()=>{const t=document.getElementById("tab-activities");t&&t.click(),isNavigating=!1},200)}catch(t){console.error("Error navigating to group:",t),isNavigating=!1}}function navigateToJourney(e){try{if(isNavigating)return;isNavigating=!0,console.log("Navigating to journey:",e),typeof window.showScreen=="function"?window.showScreen("codex"):typeof showScreen=="function"&&showScreen("codex"),window.CodexApp=window.CodexApp||{},window.CodexApp._pendingSpotlight={journeyId:e,nodeId:null};const t=()=>{if(window.CodexApp&&typeof window.CodexApp.render=="function"){const o=document.getElementById("codex-root");o&&window.CodexApp.render(o)}setTimeout(()=>{try{window.dispatchEvent(new CustomEvent("spotlight-navigate-journey",{detail:{journeyId:e,nodeId:null}}))}finally{isNavigating=!1}},500)};typeof window.loadCodexScript=="function"?window.loadCodexScript(t):setTimeout(t,500)}catch(t){console.error("Error navigating to journey:",t),isNavigating=!1}}function showOfferRedemption(e){try{console.log("[HomeWidgets] Showing offer redemption:",e);const t=widgetData.offers.find(o=>o.id===e);if(!t){console.error("[HomeWidgets] Offer not found:",e);return}typeof window.showCouponModal=="function"?window.showCouponModal(t,()=>{console.log("[HomeWidgets] Offer redeemed, refreshing widgets..."),setTimeout(()=>{loadOffers().then(()=>renderWidget())},1e3)}):alert(`Coupon Code: ${t.coupon_code} 68 69Show this to staff to redeem your offer at ${t.venue_name}`)}catch(t){console.error("[HomeWidgets] Error showing offer redemption:",t)}}function updateScrollDots(e){const t=document.getElementById("home-widget-dots");if(!t)return;if(e<=1){t.innerHTML="";return}const o=Math.min(e,5);let r="";for(let n=0;n<o;
69n++)r+=`<div class="home-widget-dot${n===0?" active":""}"></div>`;t.innerHTML=r}function setupScrollIndicator(){const e=document.getElementById("home-widget-scroll"),t=document.getElementById("home-widget-dots");!e||!t||e.addEventListener("scroll",()=>{const o=e.scrollLeft,n=Math.round(o/152);t.querySelectorAll(".home-widget-dot").forEach((d,l)=>{d.classList.toggle("active",l===n)})})}async function ensureEventsLoaded(){try{const t=Date.now();for(;!window.EventsUI&&Date.now()-t<3e3;)await new Promise(o=>setTimeout(o,100));if(!window.EventsUI||window.EventsUI.events&&window.EventsUI.events.length>0)return;window.EventsManager&&typeof window.EventsManager.loadEvents=="function"&&await window.EventsManager.loadEvents(),window.EventsUI&&typeof window.EventsUI.init=="function"&&await window.EventsUI.init()}catch(e){console.error("Error ensuring events loaded:",e)}}window.refreshHomeWidgets=async function(){await loadAllWidgetData(),renderWidget()},window.stopHomeWidgets=function(){},window.showEventFromHome=navigateToEvent,window.showActivityFromHome=navigateToGroup,window.showJourneyFromHome=navigateToJourney;
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.