1<!DOCTYPE html> <html lang="en"> <head><meta name="description" content=""> <meta charset="utf-8"> <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="msvalidate.01" content="52D8CD5F9EA2B1031F2D2472023A5D34"> <title>MyPortal | Market Reports</title> <link rel="preconnect" href="https://www.google.com"> <link rel="preconnect" href="https://www.gstatic.com" crossorigin> <link rel="dns-prefetch" href="https://cdn1.brivityidx.com">
1<script> 2 var head = document.getElementsByTagName('head')[0] 3 var insertBefore = head.insertBefore; 4 head.insertBefore = function (newElement, referenceElement) { 5 // intercept style elements for IEs 6 if (newElement.tagName.toLowerCase() === 'style' 7 && newElement.styleSheet 8 && newElement.styleSheet.cssText 9 && newElement.styleSheet.cssText.replace('\r\n', '').indexOf('.gm-style') === 0) { 10 return; 11 } 12 // intercept style elements for other browsers 13 if (newElement.tagName.toLowerCase() === 'style' 14 && newElement.innerHTML 15 && newElement.innerHTML.replace('\r\n', '').indexOf('.gm-style') === 0) { 16 return; 17 } 18 insertBefore.call(head, newElement, referenceElement); 19 }; 20 </script>
20 <link rel="shortcut icon" href="https://cdn1.brivityidx.com/assets/images/uploads/7586/icon-01.png" type="image/x-icon"> <link href="https://cdn1.brivityidx.com/assets/css/build-site.min.css?ck=" rel="stylesheet"> <link href="https://cdn1.brivityidx.com/assets/css/build-chatbot-v1.min.css?ck=" rel="stylesheet" media="print" onload="this.media='all'"><noscript><link href="https://cdn1.brivityidx.com/assets/css/build-chatbot-v1.min.css?ck=" rel="stylesheet"></noscript> <link rel="canonical" href="https://www.realestateagency.com/myportal/reports">
20<script> 21 function deferScriptOnLoad(url, sleep = 0) { 22 window.addEventListener('load', function () { 23 deferScript(url, sleep); 24 }); 25 } 26 function deferScript(url, sleep = 0) { 27 if (sleep > 0) { 28 setTimeout(() => { 29 deferScript(url); 30 }, sleep); 31 return; 32 } 33 let s = document.createElement('script'); 34 s.type = 'text/javascript'; 35 s.async = true; 36 s.src = url; 37 let x = document.getElementsByTagName('head')[0]; 38 x.appendChild(s); 39 } 40 41 /* PLACE_COMPONENTS_LOADER_START */ 42 window.sitebuilderPlaceComponents = (function () { 43 var DEFAULT_URL = 'https://mobile-component-services-library-dev.brivity.com/web-components.min.js?ck=1'; 44 var BUNDLE_URLS = { default: DEFAULT_URL }; 45 var loadPromises = {}; 46 var LOAD_TIMEOUT_MS = 10000; 47 48 function urlForTag(tagName) { 49 return BUNDLE_URLS[tagName] || BUNDLE_URLS.default; 50 } 51 52 function ensureLoaded(tagName) { 53 var url = urlForTag(tagName); 54 if (loadPromises[url]) { 55 return loadPromises[url]; 56 } 57 loadPromises[url] = new Promise(function (resolve, reject) { 58 var settled = false; 59 var timer = setTimeout(function () { 60 if (settled) return; 61 settled = true; 62 reject(new Error('web-components load timeout')); 63 }, LOAD_TIMEOUT_MS); 64 var s = document.createElement('script'); 65 s.type = 'text/javascript'; 66 s.async = true; 67 s.src = url; 68 s.onerror = function () { 69 if (settled) return; 70 settled = true; 71 clearTimeout(timer); 72 reject(new Error('web-components script onerror')); 73 }; 74 document.getElementsByTagName('head')[0].appendChild(s); 75 if (window.customElements && customElements.whenDefined) { 76 customElements.whenDefined(tagName).then(function () { 77 if (settled) return; 78 settled = true; 79 clearTimeout(timer); 80 resolve(); 81 }); 82 } 83 }); 84 return loadPromises[url]; 85 } 86 87 var EAGER_SELECTOR = 'place-propertycard, place-searchpagefilter, place-bedbathfiltermodal, ' + 88 'place-listingstatusmodal, place-propertytypemodal, place-savesearchmodal, ' + 89 'place-basicmodal, place-details, place-agentbio, place-agentmessaging, ' + 90 'place-contactyouragent, place-menu, place-collectionpopup, ' + 91 'place-copycollectionmodal, place-removecollectionpopup, place-vs-accumulate'; 92 93 function warmIfEagerTagsPresent() { 94 if (document.querySelector(EAGER_SELECTOR)) { 95 ensureLoaded('place-propertycard').catch(function () {}); 96 } 97 } 98 if (document.readyState === 'loading') { 99 document.addEventListener('DOMContentLoaded', warmIfEagerTagsPresent); 100 } else { 101 warmIfEagerTagsPresent(); 102 } 103 104 var INTERACTION_SELECTOR = 'place-getyourhomecta, place-generictoast, ' + EAGER_SELECTOR + 105 ', [data-needs-place-components]'; 106 function onInteract(e) { 107 var t = e.target; 108 if (t && t.closest && t.closest(INTERACTION_SELECTOR)) { 109 ensureLoaded('place-generictoast').catch(function () {}); 110 } 111 } 112 document.addEventListener('pointerdown', onInteract, true); 113 document.addEventListener('focusin', onInteract, true); 114 115 function idleWarm() { 116 ensureLoaded('place-generictoast').catch(function () {}); 117 } 118 if ('requestIdleCallback' in window) { 119 requestIdleCallback(idleWarm, { timeout: 5000 }); 120 } else { 121 setTimeout(idleWarm, 3000); 122 } 123 124 return { ensureLoaded: ensureLoaded }; 125 })(); 126 /* PLACE_COMPONENTS_LOADER_END */ 127 128 const googleApiKey = 'AIzaSyAjpuNg6WGCMV5rzoJcIP2QmMTVto6hYmc' 129 130 /* Lazy loader for Google Maps API + turf.js. 131 * The TURF_INLINE / MAPS_API_INLINE placeholders below are emitted by 132 * Sitemodel.php only on pages whose blocks actually consume Maps/turf. 133 * On other pages those tags are stripped, and consumers call 134 * sitebuilderMapsApi.ensureLoaded() to inject them on demand. 135 * 136 * Safari fix vs. the prior reverted attempt (commit 0cef3135e): uses 137 * Google's official &callback= parameter, not setTimeout, to detect 138 * Maps readiness. 139 */ 140 window.sitebuilderMapsApi = (function () { 141 var TURF_URL = 'https://cdn.jsdelivr.net/npm/@turf/[email protected]/turf.min.js';
142 var MAPS_URL = 'https://maps.googleapis.com/maps/api/js?libraries=geocoding,places,marker,drawing&key=' + 143 googleApiKey + '&map_ids=85c127b7bf0255d9&callback=__sitebuilderMapsReady'; 144 var LOAD_TIMEOUT_MS = 15000; 145 var loadPromise = null; 146 147 function injectScript(url) { 148 var s = document.createElement('script'); 149 s.type = 'text/javascript'; 150 s.async = true; 151 s.src = url; 152 document.getElementsByTagName('head')[0].appendChild(s); 153 return s; 154 } 155 156 function loadTurf() { 157 return new Promise(function (resolve, reject) { 158 if (window.turf) { resolve(); return; } 159 var s = injectScript(TURF_URL); 160 s.onload = function () { resolve(); }; 161 s.onerror = function () { reject(new Error('turf load failed')); }; 162 }); 163 } 164 165 function loadMaps() { 166 return new Promise(function (resolve, reject) { 167 if (window.google && window.google.maps) { resolve(); return; } 168 window.__sitebuilderMapsReady = function () { resolve(); }; 169 var s = injectScript(MAPS_URL); 170 s.onerror = function () { reject(new Error('maps load failed')); }; 171 }); 172 } 173 174 function ensureLoaded() { 175 if (loadPromise) return loadPromise; 176 if (window.google && window.google.maps && window.turf) { 177 loadPromise = Promise.resolve(); 178 return loadPromise; 179 } 180 loadPromise = new Promise(function (resolve, reject) { 181 var settled = false; 182 var timer = setTimeout(function () { 183 if (settled) return; 184 settled = true; 185 reject(new Error('maps/turf load timeout')); 186 }, LOAD_TIMEOUT_MS); 187 Promise.all([loadTurf(), loadMaps()]).then(function () { 188 if (settled) return; 189 settled = true; 190 clearTimeout(timer); 191 resolve(); 192 }, function (err) { 193 if (settled) return; 194 settled = true; 195 clearTimeout(timer); 196 reject(err); 197 }); 198 }); 199 return loadPromise; 200 } 201 202 return { ensureLoaded: ensureLoaded }; 203 })(); 204 </script>
204 <link href="https://cdn1.brivityidx.com/assets/css/style-headers.min.css" rel="stylesheet"><link href="https://cdn1.brivityidx.com/assets/css/style-premium.min.css" rel="stylesheet"><link href="https://cdn1.brivityidx.com/assets/css/library-dd.css" rel="stylesheet"><link href="https://cdn1.brivityidx.com/assets/css/style-footers.css" rel="stylesheet"><link href="https://cdn1.brivityidx.com/idx-sites/9/6/9/9695/site_custom.css?v=1790846336" rel="stylesheet">
204<script type="text/javascript" src="https://cdn1.brivityidx.com/idx-sites/9/6/9/9695/global_js_variables.js?v=1790846336"></script>
204
204<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=!0;j.src='https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f)})(window,document,'script','dataLayer','GTM-K2WGMDL')</script>
204
204<script>$(document).ready(function(){$("ul.seo-anchor-links li:nth-of-type(4) a").attr("href","/search")})</script>
204
204<script>function loadScript(a){var b=document.getElementsByTagName("head")[0],c=document.createElement("script");c.type="text/javascript",c.src="https://tracker.metricool.com/resources/be.js",c.onreadystatechange=a,c.onload=a,b.appendChild(c)}loadScript(function(){beTracker.t({hash:"f0ad558c6f6b8ff0fb0bc185cc4ca696"})})</script>
vendor: 64 bytes, line 204
204 <script async src="https://www.googletagmanager.com/gtag/js?id=
204G-SE4W6NGRBJ
vendor: 12 bytes, line 204
204"></script>
204<script> 205
vendor: 134 bytes, lines 205-209
205window.dataLayer = window.dataLayer || []; 206 function gtag(){dataLayer.push(arguments);} 207 gtag('js', new Date()); 208 209 gtag('config', '
209G-SE4W6NGRBJ
vendor: 4 bytes, line 209
209');
210</script>
210
210<script>
vendor: 395 bytes, lines 210-216
210 211!function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod? 212n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n; 213n.push=n;n.loaded=!0;n.version='2.0';n.queue=[];t=b.createElement(e);t.async=!0; 214t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}(window, 215document,'script','https://connect.facebook.net/en_US/fbevents.js'); 216fbq('init', "
216829902774477445
vendor: 30 bytes, lines 216-217
216"); 217fbq('track', 'PageView');
218var propDetailsPixelOnReady = function(){ 219 let propDetFpst = new FbRealEstatePixelTracker({"eventType": "ViewContent", "content_ids": [""]}); 220 propDetFpst.fireEvent(); 221}; 222if (document.readyState === "complete" || (document.readyState !== "loading" && !document.documentElement.doScroll)) { 223 propDetailsPixelOnReady(); 224} else { 225 document.addEventListener("DOMContentLoaded", propDetailsPixelOnReady); 226} 227 228console.log('facebook pixel PageView tracking...'); 229</script>
229 <noscript><img height="1" width="1" style="display:none" src="https://www.facebook.com/tr?id=829902774477445&ev=PageView&noscript=1"></noscript>
229<script>
vendor: 327 bytes, lines 229-233
229(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start': 230new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0], 231j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src= 232'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f); 233})(window,document,'script','dataLayer','
233GTM-NQFTPBXT
vendor: 3 bytes, line 233
233');
233</script>
233 <meta property="og:url" content="https://www.realestateagency.com/myportal/reports"> </head> <body>
vendor: 68 bytes, line 233
233 <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=
233GTM-NQFTPBXT
vendor: 83 bytes, line 233
233" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
233<script> 234 window.fbAsyncInit = function() { 235 FB.init({ 236 appId : '1', 237 cookie : true, 238 xfbml : true, 239 version : 'v17.0' 240 }); 241 FB.AppEvents.logPageView(); 242 }; 243 244 setTimeout(() => {(function(d, s, id){ 245 var js, fjs = d.getElementsByTagName(s)[0]; 246 if (d.getElementById(id)) {return;} 247 js = d.createElement(s); js.id = id; 248 js.defer = true; 249 js.xfbml = false; 250 js.src = "//connect.facebook.net/en_US/sdk.js"; 251 fjs.parentNode.insertBefore(js, fjs); 252 }(document, 'script', 'facebook-jssdk'))}, 5000); 253 </script>
253<meta property="fb:app_id" content="1"><meta name="google-signin-client_id" content="1"><meta name="apple-signin-flag" content="1"><meta name="main_sitebuilder_domain" content="https://sitebuilder.brivity.com"> <div id="site-registration-bar" class="collapse"> <input type="hidden" id="huser_id" value=""> <div class="row" style="width: 100%; margin: 0;"> <div class="col-md-12" style="display: none;" id="logged-in"> <div class="topBar flex aiCenter jcBetween"> <div class="topBarLeft flex aiCenter"> <span><topbar_show_agent_name/></span> </div> <div class="topBarRight flex aiCenter"> <a class="topLink flex aiCenter thin favoritesTop" href="/myportal/collections"> <svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg"> <g clip-path="url(#clip0_16761_179132)"> <path d="M14.2659 3.57333C13.9254 3.23267 13.5211 2.96243 13.0761 2.77806C12.6312 2.59368 12.1542 2.49878 11.6726 2.49878C11.1909 2.49878 10.714 2.59368 10.269 2.77806C9.82401 2.96243 9.41973 3.23267 9.07922 3.57333L8.37256 4.28L7.66589 3.57333C6.97809 2.88554 6.04524 2.49914 5.07256 2.49914C4.09987 2.49914 3.16702 2.88554 2.47922 3.57333C1.79143 4.26113 1.40503 5.19398 1.40503 6.16667C1.40503 7.13936 1.79143 8.07221 2.47922 8.76L3.18589 9.46667L8.37256 14.6533L13.5592 9.46667L14.2659 8.76C14.6066 8.4195 14.8768 8.01521 15.0612 7.57024C15.2455 7.12526 15.3404 6.64832 15.3404 6.16667C15.3404 5.68501 15.2455 5.20807 15.0612 4.7631C14.8768 4.31812 14.6066 3.91384 14.2659 3.57333V3.57333Z" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </g> <defs> <clipPath id="clip0_16761_179132"> <rect width="16" height="16" fill="white" transform="translate(0.372559 0.5)"/> </clipPath> </defs> </svg> <span class="ml6 topLabel">Collections </span> </a> <a class="topLink flex aiCenter thin saveSearchTop" href="/myportal/searches"> <svg width="16" height="16" viewBox="0 4 20 17" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M19 21L12 16L5 21V5C5 4.46957 5.21071 3.96086 5.58579 3.58579C5.96086 3.21071 6.46957 3 7 3H17C17.5304 3 18.0391 3.21071 18.4142 3.58579C18.7893 3.96086 19 4.46957 19 5V21Z" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /> </svg> <span class="ml6 topLabel">Saved Searches </span> </a> <a class="topLink flex aiCenter thin market-report-top" style="display:none" href="/myportal/reports"> <svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M7.70589 13.1667C10.6514 13.1667 13.0392 10.7789 13.0392 7.83333C13.0392 4.88781 10.6514 2.5 7.70589 2.5C4.76037 2.5 2.37256 4.88781 2.37256 7.83333C2.37256 10.7789 4.76037 13.1667 7.70589 13.1667Z" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M14.3725 14.5L11.4725 11.6" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </svg> <span class="ml6 topLabel">My Reports </span> </a> <a class="aiCenter flex topBarProfile thin" href="/myportal/profile"> <span class="profileName"></span> </a> <a style="padding-right: 0px" class="aiCenter flex thin" id="topBarLogout" href="javascript:void()"> <svg width="23" height="23" viewBox="0 0 23 23" fill="none" xmlns="http://www.w3.org/2000/svg"> <g clip-path="url(#clip0_16761_179186)"> <path d="M16.4003 16.214C19.0038 13.6106 19.0038 9.38945 16.4003 6.78595C13.7968 4.18246 9.57572 4.18246 6.97222 6.78595C4.36873 9.38945 4.36873 13.6105 6.97222 16.214C9.57572 18.8175 13.7968 18.8175 16.4003 16.214Z" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M9.80066 9.61438L13.5719 13.3856" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M9.80066 13.3856L13.5719 9.61438" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </g> <defs> <clipPath id="clip0_16761_179186"> <rect width="16" height="16" fill="white" transform="translate(0.372559 11.5) rotate(-45)"/> </clipPath> </defs> </svg> <span class="ml6">Log Out</span> </a> </div> </div> </div> <div id="not-logged-in"> <topbar_agent_info_not_logged_in/> <ul class="list-unstyled list-inline no-margin"> <li class="topBarSignin"><a href="#" onclick="showGoogleOneTap(true);"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-plus-circle mr10"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="16"></line>
253<line x1="8" y1="12" x2="16" y2="12"></line></svg> Sign In</a></li> </ul> </div> </div> </div> <input type="hidden" id="huser_email" value=""> <input type="hidden" id="huser_first_name" value=""> <input type="hidden" id="huser_last_name" value=""> <input type="hidden" id="huser_phone" value=""> <input type="hidden" id="huser_uuid" value=""> <div id="page" class="page"> <header id="premium-navigation" data-selector="#premium-navigation" style=""><div class="main-nav"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse" data-selector="a.btn, button.btn, button" style="outline: none; cursor: inherit;"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="/"> <img class="img-responsive site_custom_logo_dynamic" src="https://cdn1.brivityidx.com/assets/images/uploads/7586/logo-011.png" alt="Realvolution" data-selector="img" style=""></a> </div> <div class="collapse navbar-collapse"> <div> <ul class="nav navbar-nav navbar-right adv-nav"><li class="nav-item" data-selector="li" style=""><a id="top-nav-marketing-plan" class="nav-link-color" data-toggle="modal" data-target="#sell-modal">Get Marketing Plan</a></li></ul></div> <div class="basic-nav render-menu"><ul class="nav navbar-nav navbar-right"><li class="nav-item"><a class="sec-nav-link-color" href="/index">Home</a></li><li class="nav-item"><a class="sec-nav-link-color" href="/search">Search Listings</a></li><li class="nav-item"><a class="sec-nav-link-color" href="/home_value">See home value</a></li><li class="nav-item"><a class="sec-nav-link-color" href="/why_list_with_us">Why List with Us?</a></li><li class="nav-item"><a class="sec-nav-link-color" href="/careers">Careers</a></li><li class="nav-item"><a class="sec-nav-link-color" href="/who_we_are">Who We Are</a></li><li class="nav-item"><a class="sec-nav-link-color" href="/instant_offer">Instant Offer</a></li></ul></div> </div> </div> </div> </header><div class="modal fade magic-modal" id="sell-modal" tabindex="-1" role="dialog" aria-labelledby="sell-modal"> <div class="modal-dialog" role="document"> <div class="modal-content"> <div class="modal-body"> <button type="button" class="close" data-dismiss="modal" aria-label="Close" data-selector="a.btn, button.btn, button" style="outline: none; cursor: inherit;"><span aria-hidden="true">Ã</span></button> <h4 class="prmry-color" data-selector="h1, h2, h3, h4, h5, h6" style="outline: none; cursor: inherit;">Request property marketing plan</h4> <form class="form-contact" id="form-contact1"> <input type="hidden" name="lead_type" value="seller"><input type="hidden" name="tags" value="Marketing_Plan_Requested"><p data-selector="p" style="outline: none; cursor: inherit;">Youâre one step away from getting a free marketing plan that shows how weâll sell your home for the most amount of money in the least amount of time - hassle free.</p> <div class="row"> <div class="col-sm-6"> <div class="form-group"> <input name="contact1_fname" id="contact1_fname" placeholder="First Name" class="form-control" required="" type="text"></div> </div> <div class="col-sm-6"> <div class="form-group"> <input name="contact1_lname" id="contact1_lname" placeholder="Last Name" class="form-control" required="" type="text"></div> </div> <div class="col-sm-6"> <div class="form-group"> <input name="contact1_email" id="contact1_email" placeholder="Email Address" class="form-control" required="" type="email"></div> </div> <div class="col-sm-6"> <div class="form-group"> <input name="contact1_phone" id="contact1_phone" placeholder="Phone Number" class="form-control" required="" type="tel"></div> </div> <div class="col-sm-12"> <div class="form-group"> <input name="contact1_address" id="contact1_address" placeholder="Address" class="form-control" required="" type="text"></div> </div> <div class="col-sm-6"> <div class="form-group"> <input name="contact1_city" id="contact1_city" placeholder="City" class="form-control" required="" type="text"></div> </div> <div class="col-sm-6"> <div class="form-group"> <input name="contact1_state" id="contact1_state" placeholder="State" class="form-control" required="" type="text"></div> </div> <div class="col-sm-6"> <div class="form-group"> <input name="contact1_zip" id="contact1_zip" placeholder="ZIP"
253class="form-control" required="" type="text"></div> </div> <div class="col-sm-12"> <div class="form-group"> <select name="contact1_whenareyouselling" id="contact1_whenareyouselling" class="form-control" placeholder="When are you planning to sell?"><option value="" disabled selected>When are you planning to sell?</option><option value="1">Now</option><option value="2">0-6 months</option><option value="3">6+ months</option></select></div> </div> </div> <button data-dismiss="modal" class="btn btn-primary" type="submit" id="cf-submit" name="submit" data-selector="a.btn, button.btn, button" style="outline: none; cursor: inherit;">Send</button> <div id="honeypot-div"> <input name="age_required" id="age_required" value="" type="text" style="display: none;"></div></form> </div> </div> </div> </div> <style> 254 .css-1dbjc4n{ 255 z-index: unset; 256 } 257 258 .collection-page { 259 background: #f9f9f9; 260 } 261 262 .center-modal { 263 display:flex; 264 justify-content: center; 265 align-items:center; 266 position:fixed; 267 background-color:rgba(0,0,0,0.4); 268 left:0; 269 top:0; 270 right:0; 271 bottom:0; 272 z-index: 300; 273 } 274 275 #report-section{ 276 gap: 32px 277 } 278 279 .top-element-container { 280 padding: 24px 0 16px 0; 281 } 282 283 .report { 284 margin: 0 auto; 285 display:flex; 286 flex-direction:column; 287 height:100%; 288 background-color: #F9F9F9; 289 max-width: 755px; 290 } 291 292 .report-section-container { 293 padding: 16px 0 16px 0; 294 } 295 296 .page-width-parent{ 297 margin: 0 auto; 298 } 299 300 #map{ 301 background: #fff; 302 padding: 24px 32px 0px; 303 border-radius: 4px; 304 border: #E8E8E8 1px solid; 305 margin-top: 16px; 306 margin-bottom: 16px; 307 } 308 309 @media only screen and (min-width: 1280px) { 310 .collection-page { 311 padding:48px 48px 48px 120px; 312 } 313 } 314 315 @media only screen and (max-width:1279px) { 316 .collection-page { 317 padding:60px 16px 16px 16px; 318 } 319 #map{ 320 padding: 24px 12px 0px; 321 } 322 } 323 324 @media only screen and (max-width:893px) { 325 .collection-page { 326 padding: 60px 32px 16px 32px; 327 } 328 .report { 329 max-width: none; 330 } 331 } 332 333 @media only screen and (max-width:767px) { 334 place-marketreporttile{ 335 width: 100% 336 } 337 place-createmarketreport{ 338 width: 100% 339 } 340 #report-section{ 341 gap: 32px 24px 342 } 343 #map { 344 margin-top: 12px; 345 margin-bottom: 12px; 346 } 347 .collection-page { 348 padding: 60px 16px 16px 16px; 349 } 350 .top-element-container { 351 padding: 24px 0 12px 0; 352 } 353 .report { 354 padding: 0; 355 } 356 .report-section-container { 357 padding: 12px 0 12px 0; 358 } 359 } 360</style>
360<script> 361 362 let organizationUuid = "7e14a391-6849-4e22-acb5-8bf472637026"; 363 364window.addEventListener("load", () => { 365 const key = localStorage.getItem('authorization'); 366 const scrollToTop = () => { 367 window.scrollTo({ 368 top: 0, 369 }); 370 } 371 372 if( !key) { 373 $("#sign-up-modal").modal('show'); 374 375 $("#sign-up-modal").on("hidden.bs.modal", function () { 376 location.reload(); 377 }); 378 } 379 else{ 380 381 let myPortalPage = document.getElementById('myportal-page'); 382 myPortalPage.style.display = "block"; 383 384 const contact = async ( 385 firstName, 386 lastName, 387 email, 388 phone, 389 contactMessage, 390 time_event_type 391 ) => { 392 if (typeof sitebuilderRecaptcha === 'undefined' || !sitebuilderRecaptcha.ready) { 393 throw new Error("Recaptcha not loaded"); 394 } 395 let formData = new FormData(); 396 formData.append("contact_fname", firstName); 397 formData.append("contact_lname", lastName); 398 formData.append("contact_email", email); 399 formData.append("contact_phone", phone); 400 formData.append("contact_message", contactMessage); 401 formData.append("form_source_url", location.href); 402 formData.append("time_event_type", time_event_type); 403 404 const contact = await fetch("/pages/contact.php", { 405 method: "POST", 406 headers: { 407 'recaptcha-token': await sitebuilderRecaptcha.getToken('contact_form'), 408 }, 409 dataType: "json", 410 body: formData, 411 }); 412 413 return await contact.json(); 414 }; 415 416 class Pubsub { 417 static events = {}; 418 constructor() { 419 //this.events = {}; 420 } 421 422 static subscription (eventName, func) { 423 return { 424 subscribe: () => { 425 if (this.events[eventName]) { 426 this.events[eventName].push(func); 427 } else { 428 this.events[eventName] = [func]; 429 } 430 }, 431 432 unsubscribe: () => { 433 if(this.events[eventName]){ 434 this.events[eventName] = this.events[eventName].filter((subscriber) => subscriber !== func); 435 } 436 } 437 } 438 } 439 440 static publish(eventName, ...args) { 441 const funcs = this.events[eventName]; 442 if (Array.isArray(funcs)) { 443 funcs.forEach((func) => { 444 func.apply(null, args); 445 }); 446 } 447 } 448 } 449 450 class MarketHomeValueCard { 451 452 constructor(report, count) { 453 454 let _this = this; 455 this.report = report 456 this.menuOpen = false 457 this.count = count 458 this.element = document.createElement('place-marketreporttile'); 459 this.dropMenuBackdrop = document.getElementById('drop-menu-backdrop'); 460 this.element.setAttribute('market_report', JSON.stringify(report)); 461 this.element.setAttribute('z_index', count); 462 this.element.setAttribute('id', report.id); 463 this.element.setAttribute("show_menu", "false"); 464 this.element.setAttribute('drop_menu_items', JSON.stringify([ {icon:'edit-3', text: 'Update Report'}, {icon:'trash', text: 'Delete Report'} ])); 465 this.element.setAttribute('google_api_key', 'AIzaSyAjpuNg6WGCMV5rzoJcIP2QmMTVto6hYmc'); 466 467 468 this.dropMenuBackdrop.addEventListener('click', (e) => { 469 470 this.element.setAttribute("show_menu", "false"); 471 this.dropMenuBackdrop.style.display = "none"; 472 }); 473 474 475 this.element.addEventListener("on_menu_item_pressed", (e) => { 476 if(e.detail == 'Update Report'){ 477 Pubsub.publish('update_report_menu_pressed', this.report); 478 } 479 if(e.detail == 'Delete Report'){ 480 Pubsub.publish('delete_report_menu_pressed', this.report.id); 481 } 482 }); 483 484 this.element.addEventListener("on_card_pressed", (e) => { 485 window.location = `${window.origin}/myportal/report?market_report_id=${report.id}` 486 }); 487 488 this.element.addEventListener(`on_menu_open`, (e) => { 489 490 this.menuOpen = e.detail; 491 this.element.setAttribute("show_menu", this.menuOpen); 492 493 if(this.menuOpen){ 494 this.dropMenuBackdrop.style.display = 'block'; 495 this.element.setAttribute('z_index', 200); 496 } 497 else{ 498 this.dropMenuBackdrop.style.display = 'none'; 499 this.element.setAttribute('z_index', count); 500 } 501 }); 502 } 503 setName(name) { 504 this.element.setAttribute('display_name', name); 505 } 506 } 507 508 class ReportSection { 509 constructor(blueroofMiddlewareApi) { 510 511 let _this = this; 512 513 this.blueroofMiddlewareApi = blueroofMiddlewareApi; 514 this.element = document.getElementById('report-section'); 515 this.blueroofMiddlewareApi.getMarketReports().then((reports) => { 516 517 Pubsub.publish('market_reports', reports.length); 518 519 let count = 100;
520 reports.forEach((report) => { 521 count-- 522 if(report.lat_lng) { 523 this.addMarketReport(report,count) 524 } 525 else{ 526 report.lat_lng = [0,0]; 527 this.addMarketReport(report,count) 528 } 529 }) 530 }) 531 532 } 533 534 addMarketReport(report, zindex) { 535 let homeMarketValueCard = new MarketHomeValueCard(report, zindex); 536 if (report.report_name === report.location) { 537 let shortName = report?.search_location_name || report.street_address; 538 homeMarketValueCard.setName(report.report_name.length > 33 ? shortName : report.report_name); 539 } 540 541 this.element.append(homeMarketValueCard.element); 542 } 543 } 544 545 class CreateMarketReport { 546 547 constructor(blueroofMiddlewareApi) { 548 549 550 this.blueroofMiddlewareApi = blueroofMiddlewareApi; 551 this.element = document.querySelector('place-createmarketreport'); 552 553 this.element.addEventListener("on_change_text", (e) => { 554 if(e.detail.length > 0){ 555 this.blueroofMiddlewareApi.googlePlaceSuggestions(e.detail).then((suggest) => { 556 557 let suggestions = page.structureAddressSuggestions(suggest.predictions) 558 this.element.setAttribute('data', JSON.stringify(suggestions)); 559 }) 560 } 561 562 }); 563 564 this.element.addEventListener("on_selected_location", (e) => { 565 Pubsub.publish('create_market_report_selected_location', {selectedLocation: e.detail}); 566 }); 567 568 document.querySelector("place-createmarketreport input").setAttribute("autocomplete","do-not-autofill"); 569 } 570 571 show() { 572 573 this.element.setAttribute('style', 'display:flex;z-index:100') 574 } 575 576 hide() { 577 this.element.setAttribute('style', 'display:none'); 578 } 579 } 580 581 class DeleteMarketReportModal { 582 constructor(blueroofMiddlewareApi) { 583 584 this.element = document.getElementById('delete-market-report-modal'); 585 this.blueroofMiddlewareApi = blueroofMiddlewareApi; 586 this.reportId = 0 587 588 this.element.addEventListener("on_primary_press", (e) => { 589 Pubsub.publish('delete_market_report', this.reportId); 590 }); 591 592 this.element.addEventListener("on_tertiary_press", (e) => { 593 this.hide(); 594 }); 595 596 this.element.addEventListener("on_close_press", (e) => { 597 this.hide(); 598 }); 599 600 } 601 602 show(reportId) { 603 this.reportId = reportId 604 this.element.setAttribute('style', 'display:flex') 605 } 606 607 hide() { 608 this.element.setAttribute('style', 'display:none'); 609 } 610 } 611 612 class MLSErrorModal { 613 constructor() { 614 615 this.element = document.getElementById('mls-error-modal'); 616 this.element.setAttribute('sub_title', `It looks like that address is outside of your agent's MLS range. \n\nPlease contact your agent to find out more about your home value.`) 617 618 this.element.addEventListener("on_primary_press", (e) => { 619 window.location.href = '/myportal/agent' 620 }); 621 622 this.element.addEventListener("on_tertiary_press", (e) => { 623 this.hide(); 624 }); 625 626 this.element.addEventListener("on_close_press", (e) => { 627 this.hide(); 628 }); 629 630 } 631 632 show() { 633 this.element.setAttribute('style', 'display:flex') 634 } 635 636 hide() { 637 this.element.setAttribute('style', 'display:none'); 638 } 639 } 640 641 class MLSErrorMarketReportModal { 642 constructor() { 643 644 this.element = document.getElementById('mls-error-market-report-modal'); 645 this.element.setAttribute('sub_title', `Looks like weâve encountered a small hiccup, but donât worry, youâre not stuck! Try searching in your agentâs service area.`) 646 647 this.element.addEventListener("on_primary_press", (e) => { 648 this.hide(); 649 }); 650 651 this.element.addEventListener("on_close_press", (e) => { 652 this.hide(); 653 }); 654 } 655 656 show() { 657 this.element.setAttribute('style', 'display:flex') 658 } 659 660 hide() { 661 this.element.setAttribute('style', 'display:none'); 662 } 663 } 664 665 class VerifyHomeEstimateModal { 666 constructor() { 667 668 this.element = document.getElementById('verify-home-estimate-modal'); 669 this.element.setAttribute('sub_title', `Your agent will be notified and will reach out shortly to verify your home estimate!`) 670 671 this.element.addEventListener("on_primary_press", (e) => { 672 Pubsub.publish('verifyMyHomeModalClicked') 673 }); 674 675 this.element.addEventListener("on_tertiary_press", (e) => { 676 this.hide(); 677 }); 678 679 this.element.addEventListener("on_close_press", (e) => { 680 this.hide(); 681 }); 682 683 } 684 685 show() { 686 this.element.setAttribute('style', 'display:flex') 687 } 688 689 hide() { 690 this.element.setAttribute('style', 'display:none'); 691 } 692 } 693 694 class BlueroofMiddlewareApi { 695 constructor() { 696 this.baseUrl = "https://brivity-home-middleware.production.brivity.com"; 697 this.apiKey = localStorage.getItem('authorization'); 698 } 699 700 getMLSIds = async () => { 701 const response = await fetch(`${this.baseUrl}/autocomplete/mlsids`, { 702 method: 'GET', 703 headers: { 704 'Content-Type': 'application/json', 705 'Authorization': `${this.apiKey}` 706 } 707 }); 708 709 const data = await response.json(); 710 return data; 711 } 712 713 googlePlaceSuggestions = async(text) => { 714 const response = await fetch(`${this.baseUrl}/googleAutocomplete/PlaceSuggestions`, { 715 method: 'POST', 716 headers: { 717 'Content-Type': 'application/json', 718 'Authorization': `${this.apiKey}` 719 }, 720 body: JSON.stringify({ 721 input: text 722 }) 723 }); 724 725 const data = await response.json(); 726 return data; 727 } 728 729 getBrivityHomeAppInvite = async (number) => { 730 const response = await fetch(`${this.baseUrl}/brivityLeads/sendHomeAppInvite`, { 731 method: 'POST', 732 headers: { 733 'Content-Type': 'application/json', 734 'Authorization': `${this.apiKey}` 735 }, 736 body: JSON.stringify({ 737 "phoneNumber": number 738 }) 739 }); 740 741 if(!response.ok){ 742 throw new Error(); 743 } 744 745 const data = await response.json(); 746 return data; 747 } 748 749 getMarketReports = async () => { 750 const response = await fetch(`${this.baseUrl}/cma/marketReports`, { 751 method: 'GET', 752 headers: { 753 'Content-Type': 'application/json', 754 'Authorization': `${this.apiKey}` 755 } 756 }); 757 758 const data = await response.json(); 759 return data; 760 } 761 762 getMarketReport = async (id) => { 763 const response = await fetch(`${this.baseUrl}/cma/marketReport/${id}`, { 764 method: 'GET', 765 headers: { 766 'Content-Type': 'application/json', 767 'Authorization': `${this.apiKey}` 768 } 769 }); 770 771 const data = await response.json(); 772 return data; 773 } 774 775 updateMarketReport = async (id, reportData) => { 776 const response = await fetch(`${this.baseUrl}/cma/marketReport/${id}`, { 777 method: 'PATCH', 778 headers: { 779 'Content-Type': 'application/json', 780 'Authorization': `${this.apiKey}` 781 }, 782 body: JSON.stringify(reportData) 783 }); 784 785 if(!response.ok){ 786 throw new Error(); 787 } 788 789 const data = await response; 790 return data; 791 } 792 793 deleteMarketReport = async (id) => { 794 const response = await fetch(`${this.baseUrl}/cma/marketReport/${id}`, { 795 method: 'DELETE', 796 headers: { 797 'Content-Type': 'application/json', 798 'Authorization': `${this.apiKey}` 799 } 800 }); 801 802 if(!response.ok){ 803 throw new Error(); 804 } 805 806 const data = await response 807 return data; 808 } 809 810 createMarketReport = async (reportData) => { 811 const response = await fetch(`${this.baseUrl}/cma/marketReport`, { 812 method: 'POST', 813 headers: { 814 'Content-Type': 'application/json', 815 'Authorization': `${this.apiKey}` 816 }, 817 body: JSON.stringify(reportData) 818 }); 819 820 if(!response.ok){ 821 throw new Error(); 822 } 823 824 const data = await response.json(); 825 return data; 826 } 827 828 homeValueReport = async (reportData) => { 829 const response = await fetch(`${this.baseUrl}/cma/homeValueReport`, { 830 method: 'POST', 831 headers: { 832 'Content-Type': 'application/json', 833 'Authorization': `${this.apiKey}` 834 }, 835 body: JSON.stringify(reportData) 836 }); 837 838 if(!response.ok){ 839 throw new Error('Error creating market report'); 840 } 841 842 const data = await response.json(); 843 return data; 844 } 845 846 previewMarketReport = async (reportData) => { 847 const response = await fetch(`${this.baseUrl}/cma/marketReport/preview`, { 848 method: 'POST', 849 headers: { 850 'Content-Type': 'application/json', 851 'Authorization': `${this.apiKey}` 852 }, 853 body: JSON.stringify(reportData) 854 }); 855 856 if(!response.ok){ 857 Pubsub.publish('previewError', response.status); 858 throw new Error(); 859 } 860 861 const data = await response.json(); 862 return data; 863 } 864 865 getGeocodeAddress = async (addressData) => { 866 const response = await fetch(`${this.baseUrl}/address/geocode`, { 867 method: 'POST', 868 headers: { 869 'Content-Type': 'application/json', 870 'Authorization': `${this.apiKey}` 871 }, 872 body: JSON.stringify(addressData) 873 }); 874 875 const data = await response.json(); 876 return data; 877 } 878 879 getGeoJson = async(addressData) => { 880 const response = await fetch(`${this.baseUrl}/geoJson/areas/censusplaces/search`, { 881 method: 'POST', 882 headers: { 883 'Content-Type': 'application/json', 884 'Authorization': `${this.apiKey}` 885 }, 886 body: JSON.stringify(addressData) 887 }); 888 889 const data = await response.json(); 890 return data; 891 } 892 893 getSlipStreamAVM = async (addressData) => { 894 const response = await fetch(`${this.baseUrl}/slipstream/avm`, { 895 method: 'POST', 896 headers: { 897 'Content-Type': 'application/json', 898 'Authorization': `${this.apiKey}` 899 }, 900 body: JSON.stringify(addressData) 901 }); 902 903 const data = await response.json(); 904 return data; 905 } 906 907 getLandVoiceAvm = async (addressData) => { 908 const response = await fetch(`${this.baseUrl}/landVoice/avm`, { 909 method: 'POST', 910 headers: { 911 'Content-Type': 'application/json', 912 'Authorization': `${this.apiKey}` 913 }, 914 body: JSON.stringify(addressData) 915 }); 916 917 const data = await response.json(); 918 return data; 919 } 920 921 getZestimate = async (addressData) => { 922 const response = await fetch(`${this.baseUrl}/zillow/zestimate`, { 923 method: 'POST', 924 headers: { 925 'Content-Type': 'application/json', 926 'Authorization': `${this.apiKey}` 927 }, 928 body: JSON.stringify(addressData) 929 }); 930 931 const data = await response.json(); 932 return data; 933 } 934 935 getMlsValue = async (mlsData) => { 936 const response = await fetch(`${this.baseUrl}/listings/stats/value`, { 937 method: 'POST', 938 headers: { 939 'Content-Type': 'application/json', 940 'Authorization': `${this.apiKey}` 941 }, 942 body:JSON.stringify(mlsData) 943 }); 944 945 const data = await response.json(); 946 return data; 947 } 948 949 getLeadBasicInfo = async () => { 950 const response = await fetch(`${this.baseUrl}/blueroofleads/basicinfo`, { 951 method: 'GET', 952 headers: { 953 'Content-Type': 'application/json', 954 'Authorization': `${this.apiKey}` 955 }, 956 957 }); 958 959 const data = await response.json(); 960 return data; 961 } 962 963 getAgentProfile = async (brivityUserId) => { 964 const response = await fetch(`${this.baseUrl}/blueroofagents/profile`, { 965 method: 'POST', 966 headers: { 967 'Content-Type': 'application/json', 968 'Authorization': `${this.apiKey}` 969 }, 970 body: `{"brivityUserId": ${brivityUserId}}` 971 972 }); 973 974 const data = await response.json(); 975 return data; 976 } 977 978 getStreetViewImage = a
978sync (addressData) => { 979 const response = await fetch(`${this.baseUrl}/googlestreetview/getstreetviewimage`, { 980 method: 'POST', 981 headers: { 982 'Content-Type': 'application/json', 983 'Authorization': `${this.apiKey}` 984 }, 985 body: JSON.stringify(addressData) 986 }); 987 988 const imageBlob = await response.blob(); 989 const imageObjectURL = URL.createObjectURL(imageBlob); 990 return imageObjectURL; 991 } 992 993 addMarketReportFrequencyUpdatedTimelineEvent = async (event) => { 994 const response = await fetch(`${this.baseUrl}/timeline/marketReportFrequencyUpdated`, { 995 method: 'Post', 996 headers: { 997 'Content-Type': 'application/json', 998 'Authorization': `${this.apiKey}` 999 }, 1000 body: JSON.stringify(event) 1001 }) 1002 1003 if(!response.ok){ 1004 throw new Error(); 1005 } 1006 1007 const data = await response.text(); 1008 return data; 1009 } 1010 1011 addTag = async (organizationUuid, tag) => { 1012 const response = await fetch(`${this.baseUrl}/cma/person/tag`, { 1013 method: 'POST', 1014 headers: { 1015 'Content-Type': 'application/json', 1016 'Authorization': `${this.apiKey}` 1017 }, 1018 body: JSON.stringify({ organizationUuid, tag }) 1019 }); 1020 1021 if (!response.ok) { 1022 throw new Error(); 1023 } 1024 } 1025 } 1026 1027 class ReportListingBrivityHomeApp { 1028 constructor() { 1029 this.element = document.getElementById('reportListingViewGetHomeApp'); 1030 1031 this.element.addEventListener('on_button_pressed', (e) => { 1032 Pubsub.publish('sendBrivityHomeAppInvite', e.detail); 1033 }) 1034 } 1035 } 1036 1037 class ReportViewBrivityHomeApp { 1038 constructor() { 1039 this.element = document.getElementById('reportViewGetHomeApp'); 1040 1041 this.element.addEventListener('on_button_pressed', (e) => { 1042 Pubsub.publish('sendBrivityHomeAppInvite', e.detail, e.target.id); 1043 }) 1044 } 1045 setIsLoading(value) { 1046 this.element.setAttribute('loading', value); 1047 } 1048 } 1049 1050 class MarketReportModal { 1051 constructor(blueroofMiddlewareApi, page) { 1052 this.element = document.getElementById('market-report-modal'); 1053 this.menuBackdrop = document.getElementById('menu-backdrop'); 1054 this.blueroofMiddlewareApi = blueroofMiddlewareApi; 1055 this.marketReport = null; 1056 this.element.setAttribute('visible', 'false'); 1057 1058 this.blueroofMiddlewareApi.getMLSIds().then((data) => { 1059 this.mlsIds = data 1060 }) 1061 1062 this.element.addEventListener('set_visible', (e) => { 1063 this.hide(); 1064 }) 1065 1066 this.menuBackdrop.addEventListener('click', (e) => { 1067 this.hide(); 1068 }) 1069 1070 this.element.addEventListener('on_change', (e) => { 1071 if(e.detail.length > 0){ 1072 this.blueroofMiddlewareApi.googlePlaceSuggestions(e.detail).then((suggest) => { 1073 1074 let suggestions = page.structureAddressSuggestions(suggest.predictions) 1075 this.element.setAttribute('data', JSON.stringify(suggestions)); 1076 }) 1077 } 1078 }) 1079 1080 this.element.addEventListener('on_selected_location', (e) => { 1081 Pubsub.publish('create_market_report_selected_location', {selectedLocation: e.detail}); 1082 }) 1083 1084 this.element.addEventListener('on_save_button_press', (e) => { 1085 Pubsub.publish('update_report_details', {'changes':e.detail, "marketReport" : this.marketReport}); 1086 }) 1087 1088 document.querySelectorAll("#market-report-modal input")[1].setAttribute("autocomplete","do-not-autofill"); 1089 } 1090 1091 show() { 1092 1093 this.element.setAttribute('visible', 'true') 1094 this.menuBackdrop.style.display = 'flex' 1095 } 1096 1097 hide() { 1098 this.element.setAttribute('visible', 'false'); 1099 this.menuBackdrop.style.display = 'none' 1100 } 1101 1102 showReportName(value){ 1103 this.element.setAttribute('show_report_name', value); 1104 } 1105 1106 setMarketReportData(data) { 1107 this.marketReport = data 1108 this.element.setAttribute('market_report', JSON.stringify(data)); 1109 } 1110 1111 } 1112 1113 class LoadingModal { 1114 constructor() { 1115 this.element = document.querySelector('place-loadingmodal'); 1116 1117 this.element.addEventListener('on_press', (e) => { 1118 this.hide(); 1119 }) 1120 } 1121 1122 show() { 1123 this.element.setAttribute('style', 'display:flex') 1124 } 1125 1126 hide() { 1127 scrollToTop(); 1128 this.element.setAttribute('style', 'display:none'); 1129 } 1130 } 1131 1132 class EmptyMarketReport { 1133 1134 constructor(blueroofMiddlewareApi, mlsIds) { 1135 this.blueroofMiddlewareApi = blueroofMiddlewareApi; 1136 this.element = document.querySelector('place-emptymarketreport') 1137 this.mlsIds = mlsIds; 1138 1139 this.element.addEventListener("on_change", (e) => { 1140 if(e.detail.length > 0){ 1141 this.blueroofMiddlewareApi.googlePlaceSuggestions(e.detail).then((suggest) => { 1142 1143 let suggestions = page.structureAddressSuggestions(suggest.predictions) 1144 this.element.setAttribute('data', JSON.stringify(suggestions)); 1145 }) 1146 } 1147 1148 }); 1149 1150 this.element.addEventListener("on_selected_location", (e) => { 1151 Pubsub.publish('create_market_report_selected_location', {selectedLocation: e.detail}); 1152 1153 }); 1154 1155 document.querySelector("place-emptymarketreport input").setAttribute("autocomplete","do-not-autofill"); 1156 } 1157 1158 show() { 1159 this.element.setAttribute('style', 'display:flex') 1160 } 1161 1162 hide() { 1163 this.element.setAttribute('style', 'display:none'); 1164 } 1165 } 1166 1167 class SavedReportSection { 1168 constructor() { 1169 this.element = document.getElementById('saved-report-section'); 1170 } 1171 1172 show() { 1173 this.element.setAttribute('style', 'display:block') 1174 } 1175 1176 hide() { 1177 this.element.setAttribute('style', 'display:none'); 1178 } 1179 } 1180 1181 class ViewReportSection{ 1182 constructor() { 1183 this.element = document.getElementById('view-report-section'); 1184 } 1185 1186 show() { 1187 this.element.setAttribute('style', 'display:flex') 1188 } 1189 1190 hide() { 1191 this.element.setAttribute('style', 'display:none'); 1192 } 1193 } 1194 1195 class Maps { 1196 constructor() { 1197 1198 let _this = this; 1199 this.element = document.querySelector('place-mapblock'); 1200 1201 this.element.addEventListener('on_link_press', (e) => { 1202 Pubsub.publish('mapLinkPressed', e.detail) 1203 }); 1204 1205 this.element.addEventListener('on_search_for_homes', (e) => { 1206 Pubsub.publish('mapLinkPressed', e.detail) 1207 }); 1208 1209 this.element.addEventListener('on_property_pressed', (e) => { 1210 let property = e.detail; 1211 let url = this.getPropertyUrl(property); 1212 property.searchUrl = url; 1213 window.open('../../' + url, '_blank'); 1214 }); 1215 } 1216 1217 getPropertyUrl = (property) => { 1218 let state = property.state; 1219 let city = this.slugify(property.city); 1220 let zip = this.slugify(property.postal_code); 1221 let address = this.slugify(property.address); 1222 let blossorId = property.blossor_id; 1223 return `homes-for-sale/${state}/${city}/${zip}/${address}/bid-${blossorId}`; 1224 } 1225 1226 slugify(text) { 1227 // replace non letter or digits by - 1228 text = text.replace(/[^\pL\d]+~u/g, '_'); 1229 // transliterate 1230 text = text.normalize('NFKD').replace(/[\u0300-\u036F]/g,'') 1231 // remove unwanted characters 1232 text = text.replace(/[^_\w]+/g, '_'); 1233 // trim 1234 text = text.trim(); 1235 // remove duplicate - 1236 text = text.replace(/_+/g, '-'); 1237 // lowercase 1238 text = text.toLowerCase(); 1239 if (!text) { 1240 return 'n-a'; 1241 } 1242 return text 1243 } 1244 } 1245 1246 class MarketReport { 1247 constructor(blueroofMiddlewareApi){ 1248 this.blueroofMiddlewareApi = blueroofMiddlewareApi; 1249
1250 this.realtorContactCTA = document.querySelector('place-realtorcontactcta'); 1251 this.homeDetails = document.querySelector('place-homedetails'); 1252 this.estimatedHomeValue = document.querySelector("place-estimatedhomevalue"); 1253 this.temperature = document.querySelector('place-markettemperatureblock'); 1254 this.marketTrends = document.querySelector('place-markettrends'); 1255 this.map = document.querySelector('place-mapblock'); 1256 this.sendMessage = document.querySelector('place-sendmessage'); 1257 this.doesMyHomeCompare = document.querySelector('place-doesmyhomecompare'); 1258 this.ownAnotherHome = document.querySelector('place-ownanotherhome'); 1259 1260 this.realtorContactCTA.addEventListener('on_press', (e) => { 1261 Pubsub.publish('contact_us') 1262 }); 1263 this.homeDetails.addEventListener('home_details_edit_button', (e) => { 1264 Pubsub.publish('editHomeFactsClicked') 1265 }); 1266 } 1267 } 1268 1269 class InstantOfferCard { 1270 constructor() { 1271 let _this = this; 1272 this.elements = document.querySelectorAll("place-instantoffercard"); 1273 this.elements.forEach((element) => { 1274 const id = element.id; 1275 element.addEventListener('on_press', (e) => { 1276 switch (id) { 1277 case "useEquityCTA" : 1278 Pubsub.publish("unlockMyEquity"); 1279 break; 1280 case "competitiveSellCTA" : 1281 Pubsub.publish("contact_us"); 1282 break; 1283 default: 1284 throw new Error("CTA with that ID doesn't exist"); 1285 } 1286 }); 1287 }); 1288 } 1289 1290 hide() { 1291 this.elements.forEach((element) => { 1292 element.parentElement.setAttribute('style', 'display:none') 1293 }); 1294 } 1295 1296 show() { 1297 this.elements.forEach((element) => { 1298 element.parentElement.setAttribute('style', 'display:block') 1299 }); 1300 } 1301 } 1302 1303 class OtherEstimatesModal { 1304 constructor() { 1305 let _this = this; 1306 this.element = document.querySelector('place-otherestimates'); 1307 1308 this.element.addEventListener("close_button", (e) => { 1309 _this.hide(); 1310 }); 1311 } 1312 1313 show() { 1314 this.element.setAttribute('style', 'display:flex') 1315 } 1316 1317 hide() { 1318 this.element.setAttribute('style', 'display:none') 1319 } 1320 1321 1322 setZillowEstimate(zillowEstimate) { 1323 this.element.setAttribute('zillow', zillowEstimate) 1324 } 1325 1326 setSlipstreamEstimate(slipstreamEstimate) { 1327 this.element.setAttribute('home_junction', slipstreamEstimate) 1328 } 1329 1330 setMlsEstimate(mlsEstimate) { 1331 this.element.setAttribute('mls', mlsEstimate) 1332 } 1333 1334 setAttomEstimate(attomEstimate) { 1335 this.element.setAttribute('attom', attomEstimate) 1336 } 1337 } 1338 1339 class EstimatedHomeValue { 1340 constructor() { 1341 1342 let _this = this; 1343 this.element = document.querySelector("place-estimatedhomevalue"); 1344 1345 this.element.addEventListener('other_estimates_btn', (e) => { 1346 Pubsub.publish('seeOtherEstimatesClicked') 1347 }); 1348 1349 this.element.addEventListener('on_press_btn', (e) => { 1350 Pubsub.publish('verifyMyHomeModalClicked') 1351 }); 1352 1353 } 1354 1355 setEstimatedHomeEquity(estimatedHomeEquity) { 1356 this.element.setAttribute('estimated_home_equity', estimatedHomeEquity) 1357 } 1358 1359 setAddress(address) { 1360 this.element.setAttribute('address', address) 1361 } 1362 1363 setLowEndPrice(lowEndPrice) { 1364 this.element.setAttribute('low_end_price', lowEndPrice) 1365 } 1366 1367 setHighEndPrice(highEndPrice) { 1368 this.element.setAttribute('high_end_price', highEndPrice) 1369 } 1370 1371 setSquareFeet(sqft) { 1372 this.element.setAttribute('sqft', sqft) 1373 } 1374 1375 setPricePerSquareFoot(pricePerSquareFoot) { 1376 this.element.setAttribute('price_per_sqft', pricePerSquareFoot) 1377 } 1378 1379 setTemperature(temperature) { 1380 this.element.setAttribute('temperature', temperature) 1381 } 1382 1383 hide() { 1384 this.element.parentElement.setAttribute('style', 'display:none') 1385 } 1386 1387 } 1388 1389 class SendMessage { 1390 constructor() { 1391 1392 let _this = this; 1393 this.element = document.querySelector('place-sendmessage'); 1394 1395 this.element.addEventListener('send_msg', (e) => { 1396 this.element.setAttribute('loading', true); 1397 1398 contact( 1399 page?.lead?.fname, page?.lead?.lname, page?.lead?.email, page?.lead?.phone, `${e.detail?.subject} <br/> ${e.detail?.message}`,'contact_form_get_prequalified' 1400 ) 1401 .then((response) => { 1402 this.element.setAttribute('loading', false); 1403 toast.show("Inquiry sent successfully. Your agent will be in touch shortly."); 1404 }) 1405 .catch((error) => { 1406 this.element.setAttribute('loading', false); 1407 toast.show("Error sending message", true); 1408 }); 1409 }); 1410 } 1411 } 1412 1413 class PageSubHeader { 1414 constructor() { 1415 1416 this.element = document.querySelector('place-pagesubheader') 1417 this.buttonDisplay = [false, false, false, false, false, false] 1418 1419 this.getHomeValueButtonIndex = 0; 1420 this.sellMyHomeButtonIndex = 1; 1421 this.contactUsButtonIndex = 2; 1422 this.EditCriteriaIndex = 3; 1423 this.SaveReportButtonIndex = 4; 1424 this.MoreMenuButtonIndex = 5; 1425 this.MobileMoreMenuButtonIndex = 6; 1426 1427 this.defaultButtonStyle = { 1428 icon_stroke : "black", 1429 type : "none", 1430 text_styles : "text-[#000] font-medium", 1431 button_prop_styles : "border-[#000] border-2 py-2 px-4" 1432 }; 1433 1434 this.invertedButtonStyle = { 1435 icon_stroke : "#FFFFFF", 1436 type : "black", 1437 text_styles : "text-[#FFFFFF] font-medium", 1438 button_prop_styles : "border-2 border-[#000] py-2 px-4" 1439 }; 1440 1441 this.buttons = [ 1442 1443 { 1444 name:"GetHomeValue", 1445 title : "Get Home Value", 1446 icon : "activity", 1447 icon_size : 16, 1448 icon_fill : "none", 1449 icon_stroke : this.defaultButtonStyle.icon_stroke, 1450 icon_stroke_width : 3, 1451 type : this.defaultButtonStyle.type, 1452 outline : true, 1453 ghost : false, 1454 text_styles : this.defaultButtonStyle.text_styles, 1455 button_prop_styles : this.defaultButtonStyle.button_prop_styles 1456 }, 1457 { 1458 name:"SellMyHome", 1459 title : "Sell My Home", 1460 icon : "home", 1461 icon_size : 16, 1462 icon_fill : "none", 1463 icon_stroke : this.defaultButtonStyle.icon_stroke, 1464 icon_stroke_width : 3, 1465 type : this.defaultButtonStyle.type, 1466 outline : true, 1467 ghost : false, 1468 text_styles : this.defaultButtonStyle.text_styles, 1469 button_prop_styles : this.defaultButtonStyle.button_prop_styles 1470 }, 1471 { 1472 name:"ContactUs", 1473 title : "Contact Us", 1474 icon : "mail", 1475 icon_size : 16, 1476 icon_fill : "none", 1477 icon_stroke : this.defaultButtonStyle.icon_stroke, 1478 icon_stroke_width : 3, 1479 type : this.defaultButtonStyle.type, 1480 outline : true, 1481 ghost : false, 1482 text_styles : this.defaultButtonStyle.text_styles, 1483 button_prop_styles : this.defaultButtonStyle.button_prop_styles 1484 }, 1485 { 1486 name:"EditCriteria", 1487 title : "Edit Criteria", 1488 icon : "", 1489 icon_size : 16, 1490 icon_fill : "none", 1491 icon_stroke : this.defaultButtonStyle.icon_stroke, 1492 icon_stroke_width : 3, 1493 type : this.defaultButtonStyle.type, 1494 outline : true, 1495 ghost : false, 1496 text_styles : this.defaultButtonStyle.text_styles, 1497 button_prop_styles : this.defaultButtonStyle.button_prop_styles 1498 }, 1499 { 1500 name:"SaveReport", 1501 title : "Save Report", 1502 icon : "", 1503 icon_size : 16, 1504 icon_fill : "none", 1505 icon_stroke : this.invertedButtonStyle.icon_stroke, 1506 icon_stroke_width : 3, 1507 type : this.invertedButtonStyle.type, 1508 outline : true, 1509 ghost : false, 1510 text_styles : `${this.invertedButtonStyle.text_styles} ml-1`, 1511 button_prop_styles : `${this.invertedButtonStyle.button_prop_styles} mr-0` 1512 }, 1513 { 1514 name:"MoreMenuButton", 1515 title : "More", 1516 type: this.invertedButtonStyle.type, 1517 is_menu_button : true, 1518 icon_size : 25, 1519 icon_stroke : this.invertedButtonStyle.icon_stroke, 1520 icon_stroke_width : 1.5, 1521 text_styles : `${this.invertedButtonStyle.text_styles} ml-1`, 1522 button_prop_styles : `${this.invertedButtonStyle.button_prop_styles} mr-0`, 1523 menu_button_items : [ 1524 {icon:'edit-3', text: 'Update Report'}, 1525 {icon:'trash', text: 'Delete Report'} 1526 ] 1527 }, 1528 { 1529 name:"MobileMoreMenuButton", 1530 title : "More", 1531 type: this.invertedButtonStyle.type, 1532 is_menu_button : true, 1533 icon_size : 25, 1534 icon_stroke : this.invertedButtonStyle.icon_stroke, 1535 icon_stroke_width : 1.5, 1536 text_styles : `${this.invertedButtonStyle.text_styles} ml-1`, 1537 button_prop_styles : `${this.invertedButtonStyle.button_prop_styles} mr-0`, 1538 menu_button_items : [ 1539 {icon:'activity', text: 'Get Home Value'}, 1540 {icon:'home', text: 'Sell My Home'}, 1541 {icon:'mail', text: 'Contact Us'}, 1542 {icon:'edit-3', text: 'Update Report'} 1543 ] 1544 } 1545 ] 1546 1547 this.element.setAttribute('buttons', JSON.stringify(this.buttons)) 1548 this.element.setAttribute('dynamic_buttons_display', JSON.stringify(this.buttonDisplay)) 1549 this.element.setAttribute('title_styles', 'uppercase') 1550 1551 this.element.addEventListener('dynamic_button_on_press', (event) => { 1552 let buttonName = event.detail 1553 1554 switch(buttonName){ 1555 case 'GetHomeValue': 1556 Pubsub.publish('get_home_value') 1557 break; 1558 case 'SellMyHome': 1559 Pubsub.publish('sell_my_home') 1560 break; 1561 case 'ContactUs': 1562 Pubsub.publish('contact_us') 1563 break; 1564 case 'SaveReport': 1565 Pubsub.publish('save_report'); 1566 break; 1567 } 1568 1569 }) 1570 1571 this.element.addEventListener('dynamic_on_menu_item_pressed', (e) => { 1572 Pubsub.publish('top_header_menu_item_pressed', e.detail) 1573 }) 1574 1575 this.element.addEventListener('top_header_on_press', (e) => { 1576 Pubsub.publish('top_header_back_pressed', e) 1577 }) 1578 } 1579 1580 invertButtonStyles(name, invert){ 1581 this.buttons.forEach(button => { 1582 if (button.name === name) { 1583 button.icon_stroke = invert ? this.invertedButtonStyle.icon_stroke : this.defaultButtonStyle.icon_stroke; 1584 button.type = invert ? this.invertedButtonStyle.type : this.defaultButtonStyle.type; 1585 button.text_styles = invert ? this.invertedButtonStyle.text_styles : this.defaultButtonStyle.text_styles; 1586 button.button_prop_styles = invert ? this.invertedButtonStyle.button_prop_styles : this.defaultButtonStyle.button_prop_styles; 1587 } 1588 }); 1589 this.updateButtonStyles(); 1590 } 1591 1592 updateButtonStyles(){ 1593 const buttonData = this.buttons.map(button => ({ 1594 ...button, 1595 name: button.name, 1596 icon_stroke: button.icon_stroke, 1597 type: button.type, 1598 text_styles: button.text_styles, 1599 button_prop_styles: button.button_prop_styles 1600 })); 1601 this.element.setAttribute('buttons', JSON.stringify(buttonData)); 1602 } 1603 1604 setMenuItemStyles(twStyles) { 1605 this.element.setAttribute('dynamic_button_menu_styles', twStyles) 1606 } 1607 1608 setTitle(title) { 1609 this.element.setAttribute('title', title) 1610 } 1611 1612 setTopHeaderTitle(title){ 1613 this.element.setAttribute('top_header_title', title) 1614 } 1615 1616 setCriteria(criteria){ 1617 this.element.setAttribute('criteria', criteria) 1618 } 1619 1620 showTopHeader(){ 1621 this.element.setAttribute('show_top_header', true) 1622 } 1623 1624 hideTopHeader(){ 1625 this.element.setAttribute('show_top_header', false) 1626 } 1627 1628 showGetHomeValueButton() { 1629 this.buttonDisplay[this.getHomeValueButtonIndex] = true 1630 this.setDynamicButtonDisplay() 1631 } 1632 1633 hideGetHomeValueButton() { 1634 this.buttonDisplay[this.getHomeValueButtonIndex] = false 1635 this.setDynamicButtonDisplay() 1636 } 1637 1638 showSellMyHomeButton() { 1639 this.buttonDisplay[this.sellMyHomeButtonIndex] = true 1640 this.setDynamicButtonDisplay() 1641 } 1642
1643 hideSellMyHomeButton() { 1644 this.buttonDisplay[this.sellMyHomeButtonIndex] = false 1645 this.setDynamicButtonDisplay() 1646 } 1647 1648 showContactUsButton() { 1649 this.buttonDisplay[this.contactUsButtonIndex] = true 1650 this.setDynamicButtonDisplay() 1651 } 1652 1653 hideContactUsButton() { 1654 this.buttonDisplay[this.contactUsButtonIndex] = false 1655 this.setDynamicButtonDisplay() 1656 } 1657 1658 showEditCriteriaButton() { 1659 this.buttonDisplay[this.EditCriteriaIndex] = true 1660 this.setDynamicButtonDisplay() 1661 } 1662 1663 hideEditCriteriaButton() { 1664 this.buttonDisplay[this.EditCriteriaIndex] = false 1665 this.setDynamicButtonDisplay() 1666 } 1667 1668 showSaveReportButton() { 1669 this.buttonDisplay[this.SaveReportButtonIndex] = true 1670 this.setDynamicButtonDisplay() 1671 } 1672 1673 hideSaveReportButton() { 1674 this.buttonDisplay[this.SaveReportButtonIndex] = false 1675 this.setDynamicButtonDisplay() 1676 } 1677 1678 showMoreMenuButton() { 1679 this.buttonDisplay[this.MoreMenuButtonIndex] = true 1680 this.setDynamicButtonDisplay() 1681 } 1682 1683 hideMoreMenuButton() { 1684 this.buttonDisplay[this.MoreMenuButtonIndex] = false 1685 this.setDynamicButtonDisplay() 1686 } 1687 1688 showMobileMoreMenuButton(marketReport) { 1689 if(marketReport){ 1690 this.buttons[this.MobileMoreMenuButtonIndex].menu_button_items =[ 1691 {icon:'home', text: 'Sell My Home'}, 1692 {icon:'mail', text: 'Contact Us'}, 1693 {icon:'bell', text: 'Manage Alerts'}, 1694 {icon:'trash', text: 'Delete Report'} 1695 ] 1696 this.element.setAttribute('buttons', JSON.stringify(this.buttons)) 1697 } 1698 this.buttonDisplay[this.MobileMoreMenuButtonIndex] = true 1699 this.setDynamicButtonDisplay() 1700 } 1701 1702 hideMobileMoreMenuButton() { 1703 this.buttonDisplay[this.MobileMoreMenuButtonIndex] = false 1704 this.setDynamicButtonDisplay() 1705 } 1706 1707 setDynamicButtonDisplay(){ 1708 this.element.setAttribute('dynamic_buttons_display', `[${this.buttonDisplay}]`) 1709 } 1710 } 1711 1712 class HomeEquityCalculator { 1713 constructor(lead) { 1714 this.equityCalculator = document.querySelector('place-homeequitycalculator') 1715 this.equityModal = document.querySelector('place-useyourequitymodal') 1716 1717 this.equityCalculator.addEventListener("refine_sell_your_home_calculation", (e) => { 1718 this.setEquityModalAttributes() 1719 this.equityModal.setAttribute('selected_tab', 0) 1720 this.equityModal.setAttribute('style', 'display:flex;'); 1721 }) 1722 1723 this.equityCalculator.addEventListener("refine_cash_out_refinance_calculation", (e) => { 1724 this.setEquityModalAttributes() 1725 this.equityModal.setAttribute('selected_tab', 1) 1726 this.equityModal.setAttribute('style', 'display:flex;'); 1727 }) 1728 1729 this.equityCalculator.addEventListener("refine_pull_out_equity_calculation", (e) => { 1730 this.setEquityModalAttributes() 1731 this.equityModal.setAttribute('selected_tab', 2) 1732 this.equityModal.setAttribute('style', 'display:flex;'); 1733 }) 1734 1735 this.equityCalculator.addEventListener("refine_purchase_new_home_calculation", (e) => { 1736 this.setEquityModalAttributes() 1737 this.equityModal.setAttribute('selected_tab', 3) 1738 this.equityModal.setAttribute('style', 'display:flex;'); 1739 }) 1740 1741 this.equityCalculator.addEventListener("inputs_changed", (e) => { 1742 this.homeSalePrice = e.detail.homeSalePrice; 1743 this.remainingLoanBalance = e.detail.remainingLoanBalance 1744 1745 this.setEquityModalAttributes() 1746 }) 1747 1748 this.equityModal.addEventListener('on_close', () => { 1749 this.equityModal.setAttribute('style', 'display:none;'); 1750 }) 1751 1752 this.equityModal.addEventListener('get_your_free_offer_clicked', () => { 1753 this.disableButtons() 1754 1755 contact( 1756 lead?.data?.fname, lead?.data?.lname, lead?.data?.email, lead?.data?.phone, "I'd like to discu
1756ss a cash offer for my home.", 1757 ) 1758 .then((response) => { 1759 if (response.status == false) { 1760 toast.show("Error sending message", true); 1761 } 1762 else { 1763 toast.show("Discuss cash offer request sent! Your agent will be in touch shortly."); 1764 } 1765 }) 1766 .catch((error) => { 1767 toast.show("Error sending message", true); 1768 }) 1769 .finally(() => { 1770 Pubsub.publish('verifyMyHomeModalClickedCompleted') 1771 }) 1772 }) 1773 1774 this.equityModal.addEventListener('unlock_my_equity_clicked', () => { 1775 this.disableButtons() 1776 Pubsub.publish("verifyMyHomeModalClicked"); 1777 }) 1778 1779 this.equityModal.addEventListener('debt_calculator_clicked', () => { 1780 }) 1781 1782 this.equityModal.addEventListener('refine_equity_estimate_clicked', () => { 1783 }) 1784 1785 Pubsub.subscription('verifyMyHomeModalClickedCompleted' , () => { 1786 this.enableButtons() 1787 }).subscribe(); 1788 1789 } 1790 1791 disableButtons() { 1792 this.equityModal.setAttribute('disable_buttons', true) 1793 } 1794 1795 enableButtons() { 1796 this.equityModal.setAttribute('disable_buttons', false) 1797 } 1798 1799 setEquityModalAttributes() { 1800 this.equityModal.setAttribute('home_sale_price', this.homeSalePrice) 1801 this.equityModal.setAttribute('remaining_loan_balance',this.remainingLoanBalance) 1802 } 1803 1804 setAttributes(sellPrice) { 1805 this.homeSalePrice = sellPrice 1806 this.remainingLoanBalance = 0; 1807 this.equityCalculator.setAttribute('home_sale_price', sellPrice) 1808 this.equityModal.setAttribute('home_sale_price', sellPrice) 1809 } 1810 1811 show() { 1812 this.equityCalculator.setAttribute('style', 'display:block'); 1813 this.equityCalculator.parentElement.setAttribute('style', 'display:block'); 1814 } 1815 1816 hide() { 1817 this.equityCalculator.setAttribute('style', 'display:none'); 1818 this.equityCalculator.parentElement.setAttribute('style', 'display:none'); 1819 } 1820 1821 1822 } 1823 1824 class Page { 1825 constructor(blueroofMiddlewareApi){ 1826 this.isNewReport = false 1827 this.isEditingReport = false 1828 this.isReportSaved = false 1829 this.reportId = '' 1830 this.isViewingReport = false 1831 this.isReportLoaded = false 1832 this.lead = {} 1833 this.agent = {} 1834 this.mlsIds = [] 1835 this.marketReport = {} 1836 1837 this.bedOffset = 1 1838 this.bathOffset = 1 1839 this.sqftOffset = 500 1840 this.yearBuiltOffset = 1 1841 1842 this.authorizationData = JSON.parse(atob(blueroofMiddlewareApi.apiKey.split('.')[1])); 1843 this.is_blueroof_person = this.authorizationData?.is_blueroof_person; 1844 1845 window.onresize = function(event) { 1846 Pubsub.publish('window_resize') 1847 }; 1848 } 1849 1850 structureAddressSuggestions(suggestedLocations) { 1851 let suggestions = []; 1852 suggestedLocations.forEach((suggestion) => { 1853 let splitLocation = suggestion.description.split(', ') 1854 let address = '' 1855 let city = '' 1856 let state = '' 1857 let postalCode = '' 1858 if(suggestion.types[0] == 'locality'){ 1859 city = splitLocation[0]; 1860 state = splitLocation[1]; 1861 } 1862 else if(suggestion.types[0] == 'street_address' || suggestion.types[0] == 'route' || suggestion.types[0] == 'premise'){ 1863 1864 if(splitLocation.length == 4){ 1865 address = splitLocation[0]; 1866 city = splitLocation[1]; 1867 state = splitLocation[2]; } 1868 else{ 1869 address = splitLocation[1]; 1870 city = splitLocation[2]; 1871 state = splitLocation[3]; 1872 } 1873 1874 // if zip code is included in the address split it from state 1875 if(state.length > 2) { 1876 postalCode = state.split(' ').slice(1).join(' ') 1877 state = state.split(' ')[0]; 1878 } 1879 } 1880 else if(suggestion.types[0] == 'postal_code'){ 1881 1882 city = splitLocation[0]; 1883 state = splitLocation[1].split(' ')[0]; 1884 postalCode = splitLocation[1].split(' ')[1]; 1885 } 1886 1887 suggestions.push({text:suggestion.description,address:address,city:city,state:state,postalCode:postalCode, suggestType: suggestion.types[0] }); 1888 1889 }) 1890 1891 return suggestions; 1892 } 1893 } 1894 1895 class Estimates { 1896 constructor(blueroofMiddlewareApi) { 1897 this.blueroofMiddlewareApi = blueroofMiddlewareApi; 1898 } 1899 1900 getSlipstreamEstimate = async (state, city, address, zipCode) => { 1901 1902 const slipstreamData = { 1903 deliveryLine: address || "", 1904 city: city || "", 1905 state: state || "", 1906 zip: zipCode || "" 1907 } 1908 1909 let response = await this.blueroofMiddlewareApi.get
1909SlipStreamAVM(slipstreamData) 1910 this.slipstreamEstimate = response?.result?.valuations?.general?.EMV || 0 1911 this.lotSize = response?.result?.attributes?.size || 0 1912 this.beds = response?.result?.attributes?.beds || 0 1913 this.baths = response?.result?.attributes?.baths || 0 1914 this.sqft = response?.result?.attributes?.size || 0 1915 this.yearBuilt = response?.result?.attributes?.yearBuilt || 0 1916 1917 return this.slipstreamEstimate 1918 } 1919 1920 getLandVoiceEstimate = async(state, city, address, zipCode) => { 1921 const landVoiceData = { 1922 streetAddress: address || "", 1923 city: city || "", 1924 state: state || "", 1925 postalCode :zipCode || "" 1926 } 1927 1928 let response = await this.blueroofMiddlewareApi.getLandVoiceAvm(landVoiceData) 1929 this.landVoiceEstimate = response?.avm_records?.slice(-1)[0].estimated_value || 0 1930 this.address = response?.property_info?.address_full 1931 this.lotSize = response?.property_info?.acreage || 0 1932 this.beds = response?.property_info?.bedrooms || 0 1933 this.baths = response?.property_info?.bathrooms || 0 1934 this.sqft = response?.property_info?.square_footage || 0 1935 1936 return this.landVoiceEstimate 1937 1938 } 1939 1940 getZillowEstimate = async (state, city, address, zipCode) => { 1941 const zillowData = { 1942 address: address || "", 1943 city: city || "", 1944 state: state || "", 1945 postalCode: zipCode || "" 1946 } 1947 1948 let response = await this.blueroofMiddlewareApi.getZestimate(zillowData) 1949 this.zillowEstimate = response?.bundle[0]?.zestimate || 0 1950 1951 return this.zillowEstimate 1952 } 1953 1954 getMlsEstimate = async (beds, baths, sqft, streetAddress, city, state, zipCode) => { 1955 1956 const mlsData = { 1957 maxBeds : beds, 1958 maxBaths : baths, 1959 maxSqft : sqft, 1960 streetAddress : `${streetAddress}, ${city}, ${state} ${zipCode}` 1961 } 1962 1963 let response = await this.blueroofMiddlewareApi.getMlsValue(mlsData) 1964 this.mlsEstimate = response?.averagePrice; 1965 1966 return this.mlsEstimate 1967 1968 } 1969 } 1970 1971 class EditHomeFactsModal { 1972 constructor(blueroofMiddlewareApi, page) { 1973 let _this = this; 1974 this.element = document.querySelector('place-edithomefactsmodal'); 1975 this.page = page; 1976 this.blueroofMiddlewareApi = blueroofMiddlewareApi; 1977 this.reportData = null; 1978 1979 this.blueroofMiddlewareApi.getMLSIds().then((data) => { 1980 this.mlsIds = data 1981 }) 1982 1983 this.element.addEventListener('close_button', (e) => { 1984 _this.hide(); 1985 }); 1986 1987 this.element.addEventListener("cancel_button", (e) => { 1988 _this.hide(); 1989 }); 1990 1991 this.element.addEventListener('save_facts', (e) => { 1992 const { beds, baths, sqft, year_built } = e.detail; 1993 1994 let yearBuiltValue = year_built ? parseInt(year_built) : null; 1995 let changes = { 1996 ...e.detail, 1997 year_built: yearBuiltValue 1998 }; 1999 2000 let reportData = this.reportData; 2001 let slipStreamData = { 2002 min_beds: parseInt(beds) - (page?.bedOffset || 1), 2003 max_beds: parseInt(beds) + (page?.bedOffset || 1), 2004 min_baths: parseInt(baths) - (page?.bathOffset || 1), 2005 max_baths: parseInt(baths) + (page?.bathOffset || 1), 2006 min_area: parseInt(typeof sqft == "string" ? sqft.replace(/,/g,'') : sqft) - (page?.sqftOffset || 500), 2007 max_area: parseInt(typeof sqft == "string" ? sqft.replace(/,/g,'') : sqft) + (page?.sqftOffset || 500), 2008 min_year_built: parseInt(year_built) - (page?.yearBuiltOffset || 1), 2009 max_year_built: parseInt(year_built) + (page?.yearBuiltOffset || 1) 2010 }; 2011 2012 Pubsub.publish('editHomeFactsModalSaved', {reportData, slipStreamData, changes}); 2013 _this.hide(); 2014 }); 2015 } 2016 2017 setHomeFacts(data) { 2018 let page = this.page; 2019 this.reportData = data; 2020 const { min_beds, min_baths, min_area, min_year_built } = data; 2021 this.element.setAttribute('home_facts', JSON.stringify({beds: min_beds + (page?.bedOffset || 1), baths: min_baths + (page?.bathOffset || 1), sqft: min_area + (page?.sqftOffset || 500), year_built: min_year_built + page.yearBuiltOffset > 1 ? min_year_built + (page?.yearBuiltOffset || 1) : null})); 2022 } 2023 2024 show() { 2025 this.element.setAttribute('style', 'display:flex') 2026 } 2027 2028 hide() { 2029 this.element.setAttribute('style', 'display:none') 2030 } 2031 } 2032 2033 class IsThisYourHomeModal { 2034 constructor(blueroofMiddlewareApi) { 2035 this.element = document.querySelector('place-isthisyourhome'); 2036 2037 this.blueroofMiddlewareApi = blueroofMiddlewareApi; 2038 2039 this.element.addEventListener('set_bed', (e) => { 2040 this.bed = e.detail 2041 }) 2042 2043 this.element.addEventListener('set_bath', (e) => { 2044 this.bath = e.detail 2045 }) 2046 2047 this.element.addEventListener('set_sqft', (e) => { 2048 this.sqft = e.detail 2049 }) 2050 2051 this.element.addEventListener('set_year_built', (e) => { 2052 this.yearBuilt = e.detail 2053 }) 2054 2055 this.element.addEventListener('on_close', (e) => { 2056 this.hide() 2057 }) 2058 2059 this.element.addEventListener('save_inputs', (e) => { 2060 this.bed = e.detail.beds; 2061 this.bath = e.detail.baths; 2062 this.sqft = e.detail.sqft; 2063 let slipStreamData = e.detail 2064 let reportData = this.reportData 2065 2066 this.hide(); 2067 Pubsub.publish('slipstream_is_this_your_home_create',{ slipStreamData, reportData}) 2068 }) 2069 2070 } 2071 2072 setImage(imageUrl){ 2073 this.element.setAttribute('image_url', imageUrl); 2074 } 2075 2076 setAddress(address){ 2077 this.element.setAttribute('address', address); 2078 } 2079 2080 setIsThisYourHome(beds,baths,sqft){ 2081 this.element.setAttribute('is_this_your_home', JSON.stringify({beds,baths,sqft})) 2082 } 2083 2084 setYearBuilt(yearBuilt){ 2085 this.element.setAttribute('year_built', yearBuilt) 2086 } 2087 2088 setPrice(price){ 2089 this.element.setAttribute('price', price) 2090 } 2091 2092 show(reportData) { 2093 this.reportData = reportData; 2094 this.element.setAttribute('style', 'display:flex') 2095 } 2096 2097 hide() { 2098 this.element.setAttribute('style', 'display:none'); 2099 } 2100 } 2101 2102 class SaveReportModal { 2103 constructor(){ 2104 this.nextReportData = null; 2105 2106 this.element = document.querySelector('place-savereportpopup'); 2107 2108 this.element.addEventListener('close_button', (e) => { 2109 this.hide() 2110 }) 2111 2112 this.element.addEventListener('cancel_button', (e) => { 2113 this.hide() 2114 }) 2115 2116 this.element.addEventListener('save_info', (e) => { 2117 Pubsub.publish('save_report_modal_save_clicked', e.detail, this.nextReportData); 2118 this.nextReportData = null; 2119 }) 2120 } 2121 2122 clearValues(value) { 2123 this.element.setAttribute('reset_values', value); 2124 } 2125 2126 isLoading(value){ 2127 this.element.setAttribute('loading', value) 2128 } 2129 2130 show(reportData) { 2131 if (reportData) { 2132 this.nextReportData = reportData; 2133 } 2134 this.element.setAttribute('style', 'display:flex') 2135 this.clearValues(false); 2136 } 2137 2138 hide() { 2139 this.element.setAttribute('style', 'display:none'); 2140 this.clearValues(true); 2141 } 2142 } 2143 2144 class UpdateReportModal { 2145 constructor(){ 2146 this.reportData = null; 2147 this.element = document.querySelector('place-updatereportpopup'); 2148 2149 this.element.addEventListener('close_button', (e) => { 2150 this.hide() 2151 }) 2152 2153 this.element.addEventListener('cancel_button', (e) => { 2154 this.hide() 2155 }) 2156 2157 this.element.addEventListener('save_info', (e) => { 2158 const { report_title, report_frequency } = e.detail; 2159 2160 let changes = e.detail; 2161 let reportData = this.reportData; 2162 let frequency = ''; 2163 2164 switch(report_frequency){ 2165 case 'Never': 2166 frequency = 'never'; 2167 break; 2168 case 'Every Week': 2169 frequency = 'every_week'; 2170 break; 2171 case 'Every 2 Weeks': 2172 frequency = 'every_2_weeks'; 2173 break; 2174 case 'Every Month': 2175 frequency = 'every_month'; 2176 break; 2177 case 'Quarterly': 2178 frequency = 'quarterly'; 2179 break; 2180 case 'Annually': 2181 frequency = 'annually'; 2182 break; 2183 } 2184 2185 let inputData = { 2186 name: report_title, 2187 report_name: report_title, 2188 recurring_frequency: frequency 2189 }; 2190 2191 Pubsub.publish('editNameAndFrequencySaved', {reportData, inputData, changes}); 2192 this.hide(); 2193 }) 2194 2195 this.element.setAttribute('report_name', 'Market Report'); 2196 } 2197 2198 setReportName(reportName) { 2199 this.element.setAttribute('report_name', reportName); 2200 } 2201 2202 setReportFrequency(recurringFrequency) { 2203 this.element.setAttribute('email_frequency', recurringFrequency); 2204 } 2205 2206 isLoading(value){ 2207 this.element.setAttribute('loading', value) 2208 } 2209 2210 show(reportData) { 2211 if (reportData) { 2212 this.reportData = reportData; 2213 let recurringFrequency = reportData.recurring_frequency; 2214 recurringFrequency = recurringFrequency.replace(/_/g, ' '); 2215 recurringFrequency = toTitleCase(recurringFrequency); 2216 2217 this.setReportName(reportData.report_name); 2218 this.setReportFrequency(recurringFrequency); 2219 } 2220 2221 this.element.setAttribute('style', 'display:flex'); 2222 2223 function toTitleCase(str) { 2224 return str.replace(/\w\S*/g, function(txt){ 2225 return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); 2226 }); 2227 } 2228 } 2229 2230 hide() { 2231 this.element.setAttribute('style', 'display:none'); 2232 } 2233 } 2234 2235 class FrequencyModal { 2236 constructor() { 2237 this.element = document.querySelector('place-frequencymodal'); 2238 2239 this.element.addEventListener('on_close_press', (e) => { 2240 this.hide() 2241 }) 2242 2243 this.element.addEventListener('on_save_press', (e) => { 2244 Pubsub.publish('report_frequency_modal_save_clicked', e.detail) 2245 }) 2246 } 2247 2248 show(reportData) { 2249 let description = `How often would you like to receive email updates on the ${reportData.name} report?` 2250 this.element.setAttribute('description', description); 2251 this.element.setAttribute('style', 'display:flex') 2252 } 2253 2254 hide() { 2255 this.element.setAttribute('style', 'display:none'); 2256 } 2257 } 2258 2259 class DoesMyHomeCompare { 2260 constructor(blueroofMiddlewareApi){ 2261 this.element = document.querySelector('place-doesmyhomecompare'); 2262 this.blueroofMiddlewareApi = blueroofMiddlewareApi; 2263 this.address = '' 2264 2265 this.element.addEventListener('on_selected_location', (e) => { 2266 this.address = e.detail 2267 }) 2268 2269 this.element.addEventListener('on_change', (e) => { 2270 if(e.detail.length > 0){ 2271 this.blueroofMiddlewareApi.googlePlaceSuggestions(e.detail).then((suggest) => { 2272 2273 let suggestions = page.structureAddressSuggestions(suggest.predictions) 2274 this.element.setAttribute('data', JSON.stringify(suggestions)); 2275 }) 2276 } 2277 }) 2278 2279 this.element.addEventListener('on_search', () => { 2280 if (page.isNewReport) { 2281 Pubsub.publish("save_report", this.address); 2282 } else { 2283 Pubsub.publish('create_market_report_selected_location', {selectedLocation: this.address}); 2284 } 2285 }) 2286 } 2287 2288 clearInput(value) { 2289 this.element.setAttribute('reset_values', value); 2290 } 2291 } 2292 2293 class OwnAnotherHome { 2294 2295 constructor(blueroofMiddlewareApi) { 2296 2297 2298 this.blueroofMiddlewareApi = blueroofMiddlewareApi; 2299 this.element = document.querySelector('place-ownanotherhome'); 2300 2301 this.element.addEventListener("on_change_text", (e) => { 2302 if(e.detail.length > 0){ 2303 this.blueroofMiddlewareApi.googlePlaceSuggestions(e.detail).then((suggest) => { 2304 2305 let suggestions = page.structureAddressSuggestions(suggest.predictions) 2306 this.element.setAttribute('data', JSON.stringify(suggestions)); 2307 }) 2308 } 2309 2310 }); 2311 2312 this.element.addEventListener("on_selected_location", (e) => { 2313 if (page.isNewReport) { 2314 Pubsub.publish("save_report", e.detail); 2315 } else { 2316 Pubsub.publish('create_market_report_selected_location', {selectedLocation: e.detail}); 2317 } 2318 }); 2319 2320 document.querySelector("place-createmarketreport input")?.setAttribute("autocomplete","do-not-autofill"); 2321 } 2322 2323 clearInput(value) { 2324 this.element.setAttribute('reset_values', value); 2325 } 2326 2327 show() { 2328 this.element.setAttribute('style', 'display:flex;z-index:100') 2329 } 2330 2331 hide() { 2332 this.element.setAttribute('style', 'display:none'); 2333 } 2334 } 2335 2336 class GetYourHo
2336meCTA { 2337 constructor(blueroofMiddlewareApi, page) { 2338 2339 this.element = document.querySelector('place-getyourhomecta'); 2340 this.blueroofMiddlewareApi = blueroofMiddlewareApi; 2341 this.page = page 2342 this.address = '' 2343 this.currentCTAType = '' 2344 2345 this.element.addEventListener('on_selected_location', (e) => { 2346 this.address = e.detail 2347 }) 2348 2349 this.element.addEventListener('save_inputs', (e) => { 2350 2351 if(this.currentCTAType == 'sell_my_home'){ 2352 Pubsub.publish('contact_my_agent_sell_my_home', e.detail); 2353 }else if(this.currentCTAType == 'contact_us'){ 2354 Pubsub.publish('contact_my_agent_contact_us', e.detail); 2355 } 2356 }) 2357 2358 this.element.addEventListener('on_close', () => { 2359 this.hide() 2360 }) 2361 2362 this.element.addEventListener('on_change', (e) => { 2363 if(e.detail.length > 0){ 2364 this.blueroofMiddlewareApi.googlePlaceSuggestions(e.detail).then((suggest) => { 2365 2366 let suggestions = this.page.structureAddressSuggestions(suggest.predictions) 2367 this.element.setAttribute('data', JSON.stringify(suggestions)); 2368 }) 2369 } 2370 }) 2371 2372 this.element.addEventListener('on_search', (e) => { 2373 this.hide() 2374 Pubsub.publish('create_market_report_selected_location', {selectedLocation: this.address}); 2375 }) 2376 } 2377 2378 setData(data) { 2379 this.element.setAttribute('get_home_value', JSON.stringify(data)) 2380 } 2381 2382 showGetHomeValueCTA() { 2383 this.element.setAttribute('is_get_home_value_form', true) 2384 this.element.setAttribute('is_sell_my_home_form', false) 2385 this.element.setAttribute('is_contact_us_form', false) 2386 2387 this.element.setAttribute('style', 'display:flex') 2388 } 2389 2390 showSellMyHomeCTA() { 2391 2392 this.currentCTAType = 'sell_my_home' 2393 2394 this.element.setAttribute('is_get_home_value_form', false) 2395 this.element.setAttribute('is_sell_my_home_form', true) 2396 this.element.setAttribute('is_contact_us_form', false) 2397 2398 this.element.setAttribute('style', 'display:flex') 2399 } 2400 2401 showContactUsCTA() { 2402 2403 this.currentCTAType = 'contact_us' 2404 2405 this.element.setAttribute('is_get_home_value_form', false) 2406 this.element.setAttribute('is_sell_my_home_form', false) 2407 this.element.setAttribute('is_contact_us_form', true) 2408 2409 this.element.setAttribute('style', 'display:flex') 2410 } 2411 2412 hide() { 2413 this.element.setAttribute('style', 'display:none'); 2414 } 2415 2416 2417 } 2418 2419 class messagingEmitter extends Messaging.BaseEmitter { 2420 2421 newMessage(message) { 2422 Pubsub.publish('messaging_emitter_new_message', message); 2423 } 2424 } 2425 2426 const blueroofMiddlewareApi = new BlueroofMiddlewareApi(); 2427 const page = new Page(blueroofMiddlewareApi); 2428 2429 let url = 'https://messaging.brivity.com' 2430 const messaging = new Messaging.Service(url, blueroofMiddlewareApi.apiKey, new messagingEmitter()); 2431 2432 (async () => { 2433 let lead = await blueroofMiddlewareApi.getLeadBasicInfo(); 2434 let agent = await blueroofMiddlewareApi.getAgentProfile(lead.data?.brivity_agent_id); 2435 let mlsIds = await blueroofMiddlewareApi.getMLSIds(lead.data?.brivity_agent_id); 2436 2437 2438 page.lead = lead.data; 2439 page.agent = agent.data; 2440 page.mlsIds = mlsIds 2441 2442 const menu = new Menu(page?.is_blueroof_person || false, page?.agent, blueroofMiddlewareApi.baseUrl, blueroofMiddlewareApi.apiKey); 2443 const createMarketReport = new CreateMarketReport(blueroofMiddlewareApi, page); 2444 const pageSubHeader = new PageSubHeader(); 2445 const reportSection = new ReportSection(blueroofMiddlewareApi); 2446 const reportListingBrivityHomeApp = new ReportListingBrivityHomeApp(); 2447 const reportViewBrivityHomeApp = new ReportViewBrivityHomeApp(); 2448 const deleteMarketReportModal = new DeleteMarketReportModal(blueroofMiddlewareApi); 2449 const marketReportModal = new MarketReportModal(blueroofMiddlewareApi, page); 2450 const loadingModal = new LoadingModal(); 2451 const savedReportSection = new SavedReportSection(); 2452 const viewReportSection = new ViewReportSection(); 2453 const marketReport = new MarketReport(blueroofMiddlewareApi); 2454 const estimatedHomeValue = new EstimatedHomeValue(); 2455 const emptyMarketReport = new EmptyMarketReport(blueroofMiddlewareApi, page); 2456 const estimates = new Estimates(blueroofMiddlewareApi); 2457 const saveReportModal = new SaveReportModal(); 2458 const updateReportModal = new UpdateReportModal(); 2459 const isThisYourHomeModal = new IsThisYourHomeModal(blueroofMiddlewareApi); 2460 const frequencyModal = new FrequencyModal(); 2461 const editHomeFactsModal = new EditHomeFactsModal(blueroofMiddlewareApi, page); 2462 const verifyHomeEstimateModal = new VerifyHomeEstimateModal(); 2463 const otherEstimatesModal = new OtherEstimatesModal(); 2464 const mlsErrorModal = new MLSErrorModal(); 2465 const mlsErrorMarketReportModal = new MLSErrorMarketReportModal(); 2466 const sendMessage = new SendMessage(); 2467 const instantOfferCard = new InstantOfferCard(); 2468 const maps = new Maps(); 2469 const getYourHomeCTA = new GetYourHomeCTA(blueroofMiddlewareApi,page); 2470 const homeEquityCalculator = new HomeEquityCalculator(lead) 2471 const doesMyHomeCompare = new DoesMyHomeCompare(blueroofMiddlewareApi); 2472 const ownAnotherHome = new OwnAnotherHome(blueroofMiddlewareApi); 2473 2474 function loadReport (value, showLoading) { 2475 doesMyHomeCompare.clearInput(value); 2476 ownAnotherHome.clearInput(value); 2477 if (showLoading) { 2478 loadingModal.show(); 2479 } else { 2480 loadingModal.hide(); 2481 } 2482 }; 2483 2484 function handleScreenWidthSubHeaderButtons (pageSubHeader, condition, mobileMenuButtonParam) { 2485 if (screen.width < 768) { 2486 if (condition) { 2487 pageSubHeader.hideGetHomeValueButton(); 2488 pageSubHeader.showMobileMoreMenuButton(mobileMenuButtonParam); 2489 } 2490 pageSubHeader.hideContactUsButton(); 2491 pageSubHeader.hideSellMyHomeButton(); 2492 pageSubHeader.hideMoreMenuButton(); 2493 pageSubHeader.showMobileMoreMenuButton(mobileMenuButtonParam); 2494 } else { 2495 pageSubHeader.showContactUsButton(); 2496 pageSubHeader.showSellMyHomeButton(); 2497 pageSubHeader.showMoreMenuButton(); 2498 pageSubHeader.hideMobileMoreMenuButton(); 2499 } 2500 } 2501 2502 function handleMenuItemStyles () { 2503 if (screen.width < 768) { 2504 pageSubHeader.setMenuItemStyles('w-[160px] right--16'); 2505 } else { 2506 pageSubHeader.setMenuItemStyles('w-[216px] right--15'); 2507 } 2508 } 2509 2510 2511 Pubsub.subscription('window_resize', () => { 2512 2513 if(window.screen.width < 600){ 2514 pageSubHeader.element.setAttribute('dynamic_button_menu_styles', 'w-[160px] right--16') 2515 } 2516 else if(window.screen.width < 768) { 2517 pageSubHeader.element.setAttribute('inner_container_styles', 'flex-row') 2518 pageSubHeader.element.setAttribute('dynamic_button_menu_styles', 'w-[160px] right--16') 2519 } 2520 else { 2521 pageSubHeader.element.setAttribute('button_container_styles', 'flex-row') 2522 pageSubHeader.element.setAttribute('button_styles', '') 2523 pageSubHeader.element.setAttribute('inner_container_styles', 'flex-row') 2524 pageSubHeader.element.setAttribute('dynamic_button_menu_styles', 'w-[216px] right--15') 2525 } 2526 2527 if (page.isViewingReport && !page.isNewReport){ 2528 if (page.marketReport.street_address == null && screen.width > 768) { 2529 pageSubHeader.invertButtonStyles("SellMyHome", false); 2530 pageSubHeader.invertButtonStyles("MoreMenuButton", true); 2531 pageSubHeader.showGetHomeValueButton(); 2532 } else { 2533 pageSubHeader.invertButtonStyles("SellMyHome", true); 2534 pageSubHeader.hideGetHomeValueButton(); 2535 } 2536 2537 handleScreenWidthSubHeaderButtons(pageSubHeader, page.marketReport.street_address != null, page.marketReport.street_address != null); 2538 } 2539 2540 }).subscribe(); 2541 2542 Pubsub.subscription('contact_my_agent_sell_my_home', (data) =>{ 2543 getYourHomeCTA.hide(); 2544 const message = ` 2545 <p> 2546 <br/>Street Address: ${data?.street_address} 2547 <br/>City: ${data?.city} 2548 <br/>State: ${data?.state} 2549 <br/>Beds: ${data?.beds} 2550 <br/>Baths: ${data?.baths} 2551 <br/>Square Feet: ${data?.square_feet} 2552 <br/>Year Built: ${data?.year_built} 2553 <br/>Additional Comments: ${data?.message} 2554 </p> 2555 ` 2556 2557 contact( 2558 data?.first_name, data?.last_name, data?.email_address, data?.phone_number, message, 'contact_form' 2559 ) 2560 .then((response) => { 2561 toast.show("Contact information sent. Your agent will be in touch shortly. "); 2562 }) 2563 .catch((error) => { 2564 toast.show("Error sending email", true); 2565 }); 2566 2567 }).subscribe(); 2568 2569 Pubsub.subscription('contact_my_agent_contact_us', (data) =>{ 2570 getYourHomeCTA.hide(); 2571 const message = ` 2572 <p> 2573 <br/>Additional Comments: ${data?.message} 2574 </p> 2575 ` 2576 contact( 2577 data?.first_name, data?.last_name, data?.email_address, data?.phone_number, message, 'contact_form' 2578 ) 2579 .then((response) => { 2580 toast.show("Contact information sent. Your agent will be in touch shortly. "); 2581 }) 2582 .catch((error) => { 2583 toast.show("Error sending email", true); 2584 }); 2585 2586 2587 }).subscribe(); 2588 2589 Pubsub.subscription('mapLinkPressed', (e) => { 2590 const url = `/search#?q_limit=36&mlsId=${page.mls
2590Ids}&multi_search=${page.marketReport.city}, ${page.marketReport.state_abbreviation}&multi_cat=CityState&status=1|3&q_sort=createdAt-&q_offset=0` 2591 window.open(url , '_blank'); 2592 }).subscribe(); 2593 2594 Pubsub.subscription('seeOtherEstimatesClicked', (e) => { 2595 otherEstimatesModal.show(); 2596 }).subscribe(); 2597 2598 Pubsub.subscription('verifyMyHomeEstimateClicked' , (e) => { 2599 verifyHomeEstimateModal.show(); 2600 }).subscribe(); 2601 2602 Pubsub.subscription('verifyMyHomeModalClicked' , (e) => { 2603 verifyHomeEstimateModal.hide(); 2604 contact( 2605 lead?.data?.fname, lead?.data?.lname, lead?.data?.email, lead?.data?.phone, "I'd like to verify my home's estimate.", 2606 ) 2607 .then((response) => { 2608 if (response.status == false) { 2609 toast.show("Error sending message", true); 2610 } 2611 else { 2612 toast.show("Professional home valuation request sent! Your agent will be in touch shortly."); 2613 } 2614 }) 2615 .catch((error) => { 2616 toast.show("Error sending message", true); 2617 }) 2618 .finally(() => { 2619 Pubsub.publish('verifyMyHomeModalClickedCompleted') 2620 }) 2621 }).subscribe(); 2622 2623 Pubsub.subscription('getComparativeAnalysis' , (e) => { 2624 contact( 2625 lead?.data?.fname, lead?.data?.lname, lead?.data?.email, lead?.data?.phone, "Iâd like a Comparative Market Analysis report.", 2626 ) 2627 .then((response) => { 2628 if (response.status == false) { 2629 toast.show("Error sending message", true); 2630 } 2631 else { 2632 toast.show("Comparative market analysis request sent! Your agent will be in touch shortly. "); 2633 } 2634 }) 2635 .catch((error) => { 2636 toast.show("Error sending message", true); 2637 }); 2638 }).subscribe(); 2639 2640 Pubsub.subscription('unlockMyEquity' , (e) => { 2641 contact( 2642 lead?.data?.fname, lead?.data?.lname, lead?.data?.email, lead?.data?.phone, "Iâd like to discuss using my equity.", 2643 ) 2644 .then((response) => { 2645 if (response.status == false) { 2646 toast.show("Error sending message", true); 2647 } 2648 else { 2649 toast.show("Equity request sent! Your agent will be in touch shortly. "); 2650 } 2651 }) 2652 .catch((error) => { 2653 toast.show("Error sending message", true); 2654 }); 2655 }).subscribe(); 2656 2657 Pubsub.subscription('messaging_emitter_new_message', (message) => { 2658 toast.show('New message from ' + message.createdByAlias) 2659 }).subscribe(); 2660 2661 Pubsub.subscription('sendBrivityHomeAppInvite', (info, id = null) => { 2662 if (id && id == "reportViewGetHomeApp") { 2663 reportViewBrivityHomeApp.setIsLoading(true); 2664 blueroofMiddlewareApi.getBrivityHomeAppInvite(info).then((data) => { 2665 reportViewBrivityHomeApp.setIsLoading(false); 2666 if (data.error) { 2667 toast.show('Error sending invite', true) 2668 } else { 2669 toast.show('Invite has been sent!') 2670 if (organizationUuid) { 2671 blueroofMiddlewareApi.addTag(organizationUuid, "HomeAppfromMR"); 2672 } 2673 } 2674 }).catch((error) => { 2675 reportViewBrivityHomeApp.setIsLoading(false); 2676 toast.show('Error sending invite', true) 2677 }) 2678 } else { 2679 blueroofMiddlewareApi.getBrivityHomeAppInvite(info).then((data) => { 2680 if (data.error) { 2681 toast.show('Error sending invite', true) 2682 } else { 2683 toast.show('Invite has been sent!') 2684 if (organizationUuid) { 2685 blueroofMiddlewareApi.addTag(organizationUuid, "HomeAppfromMR"); 2686 } 2687 } 2688 }).catch((error) => { 2689 toast.show('Error sending invite', true) 2690 }) 2691 } 2692 }).subscribe() 2693 2694 Pubsub.subscription('edit_criteria', (report) => { 2695 marketReportModal.setMarketReportData(report || page.marketReport); 2696 marketReportModal.show(); 2697 }).subscribe() 2698 2699 Pubsub.subscription("update_report_menu_pressed",(report) => { updateReportModal.show(report) }).subscribe() 2700 2701 Pubsub.subscription("delete_report_menu_pressed",(e) => { deleteMarketReportModal.show(e) }).subscribe() 2702 2703 Pubsub.subscription("market_reports", (saved_report_length) => { 2704 if(saved_report_length == 0){ 2705 emptyMarketReport.show(); 2706 } 2707 else{ 2708 createMarketReport.show(); 2709 $("#gethomeapp-parent").css('margin', '70px 0 32px'); 2710 } 2711 }).subscribe() 2712 2713 Pubsub.subscription('top_header_back_pressed', (e) => { 2714 this.location.href=`./reports` 2715 }).subscribe(); 2716 2717 Pubsub.subscription('top_header_menu_item_pressed', (menuItem) => { 2718 if(menuItem == 'Delete Report') { 2719 deleteMarketReportModal.show(page.reportId) 2720 } 2721 if(menuItem == 'Manage Alerts') { 2722 frequencyModal.show(page.marketReport); 2723 } 2724 if(menuItem == 'Sell My Home') { 2725 Pubsub.publish('sell_my_home') 2726 } 2727 if(menuItem == 'Contact Us') { 2728 Pubsub.publish('contact_us') 2729 } 2730 if(menuItem == 'Get Home Value') { 2731 Pubsub.publish('get_home_value') 2732 } 2733 }).subscribe(); 2734 2735 Pubsub.subscription('open_market_report', (newReportData) => { 2736 (async () => { 2737 2738 loadingModal.show(); 2739 savedReportSection.hide(); 2740 pageSubHeader.showTopHeader(); 2741 2742 viewReportSection.show(); 2743 doesMyHomeCompare.clearInput(true); 2744 2745 let data = {} 2746 2747 if(newReportData.isNewReport){ 2748 page.isNewReport = true; 2749 lifecycle.removeUnsavedChanges('unsavedChanges'); 2750 lifecycle.addUnsavedChanges('unsavedChanges'); 2751 data = newReportData.reportData; 2752 2753 pageSubHeader.showSaveReportButton(); 2754 2755 marketReportModal.showReportName(false) 2756 2757 pageSubHeader.hideGetHomeValueButton(); 2758 pageSubHeader.hideSellMyHomeButton(); 2759 pageSubHeader.hideContactUsButton(); 2760 pageSubHeader.hideMoreMenuButton(); 2761 pageSubHeader.hideMobileMoreMenuButton(); 2762 } else { 2763 2764 if(!page.isReportLoaded){ 2765 data = await blueroofMiddlewareApi.getMarketReport(newReportData.reportId); 2766 page.isReportLoaded = true; 2767 } 2768 else{ 2769 data = newReportData.reportData 2770 } 2771 2772 if(data?.search_type == 'area'){ 2773 pageSubHeader.hideEditCriteriaButton(); 2774 pageSubHeader.showGetHomeValueButton(); 2775 pageSubHeader.invertButtonStyles("MoreMenuButton", true); 2776 pageSubHeader.invertButtonStyles("SellMyHome", false); 2777 } else { 2778 pageSubHeader.invertButtonStyles("MoreMenuButton", false); 2779 pageSubHeader.invertButtonStyles("SellMyHome", true); 2780 } 2781 2782 handleMenuItemStyles(); 2783 handleScreenWidthSubHeaderButtons(pageSubHeader, data?.search_type == 'area', data?.search_type != 'area'); 2784 2785 marketReportModal.showReportName(true) 2786 } 2787 2788 page.reportId = data.id 2789 page.marketReport = data; 2790 2791 2792 data?.listings?.forEach(function (listing) { 2793 let image = [] 2794
2795 listing.images.forEach(function (listingImage) { 2796 image.push ({uri: listingImage}) 2797 }) 2798 2799 listing.image = image 2800 listing.address = listing.street_address 2801 listing.sqft = listing.area 2802 2803 }); 2804 2805 2806 let reportData = { 2807 "report_name" : data.name, 2808 "city" : data.city, 2809 "property_type" : data.property_type, 2810 "min_price": parseInt(data.min_price), 2811 "max_price": parseInt(data.max_price), 2812 "min_beds": parseInt(data.min_beds), 2813 "max_beds": parseInt(data.max_beds), 2814 "min_baths": parseInt(data.min_baths), 2815 "max_baths": parseInt(data.max_baths), 2816 "min_area": parseInt(data.min_area), 2817 "max_area": parseInt(data.max_area), 2818 "min_lot": parseInt(data.min_lot), 2819 "max_lot": parseInt(data.max_lot), 2820 "min_year_built": parseInt(data.min_year_built), 2821 "max_year_built": parseInt(data.max_year_built), 2822 } 2823 2824 marketReportModal.setMarketReportData(reportData) 2825 2826 2827 let isMarketReport = true 2828 2829 if(data.street_address != null ) { 2830 isMarketReport = false 2831 } 2832 2833 pageSubHeader.setTopHeaderTitle('Market Reports') 2834 if(!isMarketReport){ 2835 2836 let title = '' 2837 if(data?.street_address && data?.city){ 2838 title = `${data?.street_address ?? ""} ${data?.city ?? ""}, ${data?.state_abbreviation ?? ""} ${data?.postal_code ?? ""}` 2839 } 2840 else{ 2841 title = data?.name 2842 } 2843 2844 pageSubHeader.setTitle(data?.search_location_name || title) 2845 pageSubHeader.setCriteria(`${data?.property_type}${data.radius ? ` - ${parseInt(data.radius)} mile radius` : ''}`) 2846 2847 2848 let beds = isThisYourHomeModal?.bed || data?.min_beds + page.bedOffset || estimates?.beds || 0 2849 let baths = isThisYourHomeModal?.bath || data?.min_baths + page.bathOffset || estimates?.baths || 0 2850 let sqft = isThisYourHomeModal?.sqft || data?.min_area + page.sqftOffset || estimates?.sqft || 0 2851 let address = estimates?.address || data?.street_address 2852 let yearBuilt = data?.min_year_built + page.yearBuiltOffset > 1 && data?.min_year_built + page.yearBuiltOffset || estimates?.yearBuilt || 0 2853 2854 let landVoiceEstimate = estimates.getLandVoiceEstimate(data?.state_abbreviation, data?.city, data?.street_address, data?.postal_code) 2855 let zillowEstimate = estimates.getZillowEstimate(data?.state_abbreviation, data?.city, address, data?.postal_code) 2856 2857 if (data && data.street_address) { 2858 data.street_address = data.street_address.toLowerCase().replace(/\b\w/g, function (char) { 2859 return char.toUpperCase(); 2860 }); 2861 } 2862 2863 if (data && data.city) { 2864 data.city = data.city.toLowerCase().replace(/\b\w/g, function (char) { 2865 return char.toUpperCase(); 2866 }); 2867 } 2868 2869 if (data && data.state_abbreviation) { 2870 data.state_abbreviation = data.state_abbreviation.toUpperCase(); 2871 } 2872 2873 Promise.allSettled([landVoiceEstimate, zillowEstimate]).then( async (values) => { 2874 divisorCount = 0 2875 if(values[0].value > 0) 2876 { 2877 divisorCount++ 2878 } 2879 if(values[1].value > 0) 2880 { 2881 divisorCount++ 2882 } 2883 2884 2885 let averageEstimate 2886 if(divisorCount > 0){ 2887 averageEstimate = parseInt((parseInt(values[0]?.value || 0
2887) + parseInt(values[1]?.value || 0)) / divisorCount) 2888 otherEstimatesModal.setAttomEstimate(parseInt(values[0].value)) 2889 otherEstimatesModal.setZillowEstimate(parseInt(values[1].value)) 2890 } 2891 else{ 2892 let mlsEstimate = 0 2893 try { 2894 mlsEstimate = await estimates.getMlsEstimate( 2895 beds, 2896 baths, 2897 sqft, 2898 data?.street_address, 2899 data?.city, 2900 data?.state_abbreviation, 2901 data?.postal_code 2902 ) 2903 } catch (e) { 2904 2905 } 2906 2907 if( mlsEstimate > 0) 2908 { 2909 averageEstimate = mlsEstimate 2910 otherEstimatesModal.setMlsEstimate(parseInt(mlsEstimate)) 2911 } 2912 else{ 2913 averageEstimate = 350000 2914 } 2915 } 2916 2917 let equityLow = parseInt(averageEstimate * 0.2 ) 2918 let equityHigh = parseInt(averageEstimate * 0.3) 2919 let estimatedHomeSellPrice = averageEstimate 2920 let remainingBalance = 50000 2921 let estimatedHomeValueOffset = Math.round(averageEstimate * 0.10) 2922 let homeAddress = `${data?.street_address ?? ""} ${data?.city ?? ""}, ${data?.state_abbreviation ?? ""} ${data?.postal_code ?? ""}` 2923 2924 marketReport.homeDetails.setAttribute('beds', beds); 2925 marketReport.homeDetails.setAttribute('baths', baths); 2926 marketReport.homeDetails.setAttribute('sqft', sqft); 2927 marketReport.homeDetails.setAttribute('build_year', yearBuilt); 2928 2929 if (estimates?.sqft) { 2930 estimatedHomeValue.setSquareFeet(estimates?.sqft); 2931 } else { 2932 estimatedHomeValue.setPricePerSquareFoot(Math.trunc(averageEstimate / sqft) || 0); 2933 } 2934 2935 estimatedHomeValue.setEstimatedHomeEquity(averageEstimate) 2936 estimatedHomeValue.setAddress(homeAddress || "") 2937 estimatedHomeValue.setLowEndPrice(averageEstimate - estimatedHomeValueOffset) 2938 estimatedHomeValue.setHighEndPrice(averageEstimate + estimatedHomeValueOffset) 2939 estimatedHomeValue.setTemperature(50 || 0) 2940 2941 homeEquityCalculator.setAttributes(estimatedHomeSellPrice) 2942 2943 marketReport.estimatedHomeValue.parentElement.setAttribute('style', 'display:block') 2944 2945 pageSubHeader.hideGetHomeValueButton(); 2946 2947 marketReport.doesMyHomeCompare.parentElement.setAttribute('style', 'display:none') 2948 marketReport.ownAnotherHome.parentElement.setAttribute('style', 'display:block') 2949 2950 instantOfferCard.show(); 2951 homeEquityCalculator.show(); 2952 2953 pageSubHeader.hideGetHomeValueButton(); 2954 2955 loadReport(false, false); 2956 2957 }); 2958 } 2959 else { 2960 marketReport.homeDetails.parentElement.setAttribute('style', 'display:none') 2961 2962 let title = '' 2963 if(data?.city){ 2964 title = `${data?.city}, ${data?.state_abbreviation} ${data?.postal_code}` 2965 } 2966 else{ 2967 title = data?.name 2968 } 2969 2970 pageSubHeader.setTitle(data?.search_location_name || title) 2971 pageSubHeader.setCriteria(`${data?.property_type}${data.radius ? ` - ${parseInt(data.radius)} mile radius` : ''}`) 2972 2973 2974 marketReport.estimatedHomeValue.parentElement.setAttribute('style', 'display:none') 2975 marketReport.ownAnotherHome.parentElement.setAttribute('style', 'display:none') 2976 marketReport.doesMyHomeCompare.parentElement.setAttribute('style', 'display:block') 2977 2978 instantOfferCard.hide(); 2979 2980 2981 homeEquityCalculator.hide() 2982 2983 loadReport(false, false) 2984 } 2985 2986 if(data?.city && data?.state_abbreviation){ 2987 marketReport.marketTrends.setAttribute('area', `${data?.city}, ${data?.state_abbreviation}`); 2988 } 2989 2990 marketReport.marketTrends.setAttribute('items',`[ 2991 { "icon": "dollar-sign", "label": "Average Price", "value": ${data?.trends?.trend_average_price || 0}}, 2992 { "icon": "zap", "label": "New Homes", "value":${data?.trends?.trend_new_listings || 0}}, 2993 { "icon": "tag", "label": "Sold Homes", "value": ${data?.trends?.trend_sold_listings || 0}}, 2994 { "icon": "calendar", "label": "Days On Market", "value": ${data?.trends?.trend_average_dom || 0}} 2995 ]`) 2996 2997 if (data?.hide_market_temp){ 2998 marketReport.temperature.parentElement.setAttribute('style', 'display:none') 2999 } else { 3000 marketReport.temperature.setAttribute('temperature', data?.market_temp || 50) 3001 marketReport.temperature.setAttribute('city', data?.city || '') 3002 marketReport.temperature.setAttribute('state', data?.state_abbreviation || '') 3003 }; 3004 3005 if(data?.geo_json){ 3006 let geo = JSON.parse(data?.geo_json)?.coordinates[0] || []; 3007 let geoArray = [] 3008 3009 for(let i = 0; i < geo.length; i++){ 3010 geoArray.push({lat: geo[i][1], lng: geo[i][0]}) 3011 } 3012 3013 marketReport.map.setAttribute('polygon_points', JSON.stringify(geoArray)) 3014 } 3015 3016 marketReport.map.setAttribute('default_zoom',
301612) 3017 marketReport.map.setAttribute('area', data.city || '') 3018 marketReport.map.setAttribute('state', data.state_abbreviation || '') 3019 3020 const mlsData = data.listings.map(listing => ({ 3021 ...listing, 3022 creator: `Listed with ${listing.listing_office}`, // Map listing_office to creator 3023 })); 3024 3025 marketReport.map.setAttribute('mls_data', JSON.stringify(mlsData)); 3026 3027 page.isViewingReport = true; 3028 3029 })(); 3030 3031 }).subscribe(); 3032 3033 Pubsub.subscription('create_market_report_selected_location', async (data) => { 3034 3035 loadReport(true, true); 3036 const selectedLocation = data.selectedLocation; 3037 const city = selectedLocation?.city; 3038 const state = selectedLocation?.state; 3039 const postalCode = selectedLocation?.postalCode; 3040 const address = selectedLocation?.address; 3041 3042 let reportData = { 3043 property_type: 'Residential', 3044 city: city, 3045 postal_code: postalCode, 3046 state_abbreviation: state, 3047 street_address: address, 3048 search_location_name: selectedLocation?.text 3049 } 3050 3051 let addressData = { 3052 streetAddress: address, 3053 city: city, 3054 state: state 3055 } 3056 3057 if (reportData.city === '' && reportData.state_abbreviation === '' && reportData.postal_code === '') { 3058 const suggestedLocation = selectedLocation?.text; 3059 const locationArr = suggestedLocation.split(', '); 3060 3061 let streetAddress, city, statePostal, stateAbbr, postalCode; 3062 3063 if (locationArr.length < 4) { 3064 [ city, statePostal ] = selectedLocation?.text.split(', '); 3065 [ stateAbbr, postalCode ] = statePostal.split(' '); 3066 } else { 3067 [ streetAddress, city, statePostal ] = selectedLocation?.text.split(', '); 3068 [ stateAbbr, postalCode ] = statePostal.split(' '); 3069 } 3070 3071 reportData = { 3072 ...reportData, 3073 street_address: streetAddress?.trim() || '', 3074 city: city?.trim() || '', 3075 state_abbreviation: stateAbbr?.trim() || '', 3076 postal_code: postalCode?.trim() || '' 3077 } 3078 3079 addressData = { 3080 streetAddress: streetAddress?.trim() || '', 3081 city: city?.trim() || '', 3082 state: stateAbbr?.trim() || '' 3083 } 3084 } 3085 3086 const geoCodeAddress = await blueroofMiddlewareApi.getGeocodeAddress(addressData); 3087 reportData.postal_code = geoCodeAddress?.postalCode; 3088 3089 let getAvmData = false; 3090 const allowedTypes = [ 3091 'street_address', 3092 'route', 3093 'premise', 3094 'subpremise', 3095 'geocode' 3096 ]; 3097 3098 if (allowedTypes.includes(selectedLocation.suggestType) && reportData.street_address) { 3099 getAvmData = true; 3100 } 3101 3102 if(getAvmData) { 3103 3104 const landVoiceData = { 3105 streetAddress: reportData?.street_address || address, 3106 city: reportData?.city || city, 3107 state: reportData?.state_abbreviation || state, 3108 postalCode: reportData?.postal_code || postalCode 3109 } 3110 3111 const avm = await blueroofMiddlewareApi.getLandVoiceAvm(landVoiceData) 3112 3113 const beds = avm?.property_info?.bedrooms 3114 const baths = avm?.property_info?.bathrooms 3115 const sqft = avm?.property_info?.square_footage 3116 const yearBuilt = avm?.property_info?.year_built 3117 const avmAddress = avm?.property_info?.address_full 3118 const latitude = avm?.property_info?.latitude 3119 const longitude = avm?.property_info?.longitude 3120 const price = avm?.avm_records?.length > 0 ? avm?.avm_records?.slice(-1)[0].estimated_value : 350000 3121 3122 3123 estimates.beds = beds; 3124 estimates.baths = baths; 3125 estimates.sqft = sqft; 3126 estimates.address = avmAddress 3127 estimates.yearBuilt = yearBuilt; 3128 3129 reportData.min_beds = beds - page.bedOffset; 3130 reportData.max_beds = beds + page.bedOffset; 3131 reportData.min_baths = baths - page.bathOffset; 3132 reportData.max_baths = baths + page.bathOffset; 3133 reportData.min_area = sqft - page.sqftOffset; 3134 reportData.max_area = sqft + page.sqftOffset; 3135 3136 reportData.recurring_frequency = "every_2_weeks", 3137 3138 blueroofMiddlewareApi.homeValueReport(reportData).then((report) => { 3139 localStorage.setItem('marketReportData', JSON.stringify(report)); 3140 window.location = `${window.origin}/myportal/report?market_report_id=${report.id}` 3141 }).catch((error) => { 3142 loadReport(false, false); 3143 }) 3144 3145 } 3146 else{ 3147 blueroofMiddlewareApi.previewMarketReport(reportData).then((data) => { 3148 data.recurring_frequency = "every_2_weeks", 3149 data.name = data?.location, 3150 blueroofMiddlewareApi.createMarketReport(data).then((report) => { 3151 localStorage.setItem('marketReportData', JSON.stringify(report)); 3152 window.location = `${window.origin}/myportal/report?market_report_id=${report.id}` 3153 }).catch((error) => { 3154 loadReport(false, false); 3155 }) 3156 }).catch((error) => { 3157 loadReport(false, false); 3158 }) 3159 } 3160 3161 }).subscribe() 3162 3163 Pubsub.subscription('delete_market_report', (reportId) => { 3164 3165 blueroofMiddlewareApi.deleteMarketReport(reportId).then((data) => { 3166 document.getElementById(reportId).remove(); 3167 deleteMarketReportModal.hide() 3168 3169 savedReportSection.show(); 3170 viewReportSection.hide(); 3171 pageSubHeader.hideTopHeader(); 3172 pageSubHeader.hideEditCriteriaButton(); 3173 pageSubHeader.hideSaveReportButton(); 3174 pageSubHeader.hideMoreMenuButton(); 3175 marketReportModal.hide(); 3176 pageSubHeader.setTitle('Market Reports'); 3177 3178 page.isNewReport = false;
3179 page.isEditingReport = false; 3180 page.isReportSaved = false; 3181 page.reportId = ''; 3182 page.isViewingReport = false; 3183 page.isReportLoaded = false 3184 3185 toast.show('Market Report Deleted') 3186 }) 3187 }).subscribe() 3188 3189 Pubsub.subscription('update_report_details', (reportData) => { 3190 3191 loadReport(true, true); 3192 const changes = reportData.changes; 3193 const marketReport = reportData.marketReport; 3194 let updatedReportData = {}; 3195 3196 updatedReportData.id = changes?.id || marketReport?.id; 3197 updatedReportData.name = changes?.report_name || marketReport?.report_name || 'Preview Market Report'; 3198 updatedReportData.report_name = changes?.report_name || marketReport?.report_name || ''; 3199 updatedReportData.mls_data_source_id = marketReport.mls_data_source_id; 3200 updatedReportData.recurring_frequency = changes?.recurring_frequency || marketReport?.recurring_frequency || 'every_2_weeks'; 3201 updatedReportData.property_type = changes?.property_type || marketReport?.property_type; 3202 updatedReportData.radius = parseInt(changes.radius) || parseInt(marketReport.radius); 3203 updatedReportData.search_type = changes?.search_type || marketReport?.search_type; 3204 updatedReportData.city = changes?.city || marketReport?.city; 3205 updatedReportData.state_abbreviation = changes?.state_abbreviation || marketReport?.state_abbreviation; 3206 updatedReportData.street_address = changes?.street_address || marketReport?.street_address; 3207 updatedReportData.postal_code = changes?.postal_code || marketReport?.postal_code; 3208 // these are broken out into if statements because 3209 // parseInt("0") return false and parseInt("1") returns true 3210 // so parseInt(changes.min_bath) || parseInt(marketReport.min_bath) will always 3211 // use the marketReport.min_bath value 3212 if( changes?.min_baths || changes?.min_baths == 0){ 3213 updatedReportData.min_baths = parseInt(changes.min_baths || 0) 3214 } 3215 else{ 3216 updatedReportData.min_baths = parseInt(marketReport.min_baths) 3217 } 3218 3219 if( changes?.max_baths ){ 3220 updatedReportData.max_baths = parseInt(changes.max_baths) 3221 } 3222 else{ 3223 updatedReportData.max_baths = parseInt(marketReport.max_baths) 3224 } 3225 3226 if( changes?.min_beds || changes?.min_beds == 0){ 3227 updatedReportData.min_beds = parseInt(changes.min_beds || 0) 3228 } 3229 else{ 3230 updatedReportData.min_beds = parseInt(marketReport.min_beds) 3231 } 3232 3233 if( changes?.max_beds ){ 3234 updatedReportData.max_beds = parseInt(changes.max_beds) 3235 } 3236 else{ 3237 updatedReportData.max_beds = parseInt(marketReport.max_beds) 3238 } 3239 3240 if( changes?.min_price ){ 3241 updatedReportData.min_price = parseInt(changes.min_price) 3242 } 3243 else{ 3244 updatedReportData.min_price = parseInt(marketReport.min_price) 3245 } 3246 3247 if( changes?.max_price ){ 3248 updatedReportData.max_price = parseInt(changes.max_price) 3249 } 3250 else{ 3251 updatedReportData.max_price = parseInt(marketReport.max_price) 3252 } 3253 3254 if( changes?.min_area || changes?.min_area == 0 ){ 3255 updatedReportData.min_area = parseInt(changes.min_area || 0) 3256 } 3257 else{ 3258 updatedReportData.min_area = parseInt(marketReport.min_area) 3259 } 3260 3261 if( changes?.max_area ){ 3262 updatedReportData.max_area = parseInt(changes.max_area) 3263 } 3264 else{ 3265 updatedReportData.max_area = parseInt(marketReport.max_area) 3266 } 3267 3268 if( changes?.min_lot ){ 3269 updatedReportData.min_lot = parseInt(changes.min_lot) 3270 } 3271 else{ 3272 updatedReportData.min_lot = parseInt(marketReport.min_lot) 3273 } 3274 3275 if( changes?.max_lot ){ 3276 updatedReportData.max_lot = parseInt(changes.max_lot) 3277 } 3278 else{ 3279 updatedReportData.max_lot = parseInt(marketReport.max_lot) 3280 } 3281 3282 if(marketReport.search_type == 'area'){ 3283 updatedReportData.geo_json = changes?.geo_json || marketReport?.geo_json; 3284 3285 } 3286 else { 3287 updatedReportData.lat_lng = changes?.lat_lng || marketReport?.lat_lng; 3288 } 3289 3290 let currentMarketReportTile = document.getElementById(updatedReportData.id); 3291 3292 blueroofMiddlewareApi.updateMarketReport(updatedReportData.id, updatedReportData).then((data) => { 3293 page.marketReport = { 3294 ...page.marketReport, 3295 ...updatedReportData 3296 }; 3297 3298 let displayData = { 3299 ...reportData.marketReport, 3300 "report_name" : updatedReportData.name, 3301 }; 3302 3303 currentMarketReportTile?.setAttribute('market_report', JSON.stringify(displayData)); 3304 toast.show('Market Report Saved') 3305 3306 if(updatedReportData.recurring_frequency != marketReport.recurring_frequency){ 3307 let data = {reportName: updatedReportData.name, dripSchedule:updatedReportData.recurring_frequency} 3308 blueroofMiddlewareApi.addMarketReportFrequencyUpdatedTimelineEvent(data ) 3309 } 3310 }).catch((e) => { 3311 toast.show('Error saving market report', true) 3312 }) 3313 3314 loadReport(false, false); 3315 3316 }).subscribe() 3317 3318 Pubsub.subscription('save_report_modal_save_clicked', (data, nextReportData) => { 3319 3320 3321 let name = data.report_title 3322 let frequency; 3323 3324 //valid options for frequency { never, every_week, every_2_weeks, every_month, quarterly, annually } 3325 switch(data.report_frequency){ 3326 case 'Never': 3327 frequency = 'never'; 3328 break; 3329 case 'Every Week': 3330 frequency = 'every_week'; 3331 break; 3332 case 'Every 2 Weeks': 3333 frequency = 'every_2_weeks'; 3334 break; 3335 case 'Every Month': 3336 frequency = 'every_month'; 3337 break; 3338 case 'Quarterly': 3339 frequency = 'quarterly'; 3340 break; 3341 case 'Annually': 3342 frequency = 'annually'; 3343 break; 3344 } 3345 3346 let newSavedMarketReport = { 3347 mls_data_source_id : page?.marketReport?.mls_data_source_id, 3348 property_type: page?.marketReport?.property_type, 3349 radius: parseInt(page?.marketReport?.radius), 3350 search_type: page?.marketReport?.search_type, 3351 recurring_frequency : frequency, 3352 geo_json: page?.marketReport?.geo_json, 3353 city: page?.marketReport?.city, 3354 lat_lng: page?.marketReport?.lat_lng, 3355 min_area: parseInt(page?.marketReport?.min_area ), 3356 min_baths: parseInt(page?.marketReport?.min_baths ), 3357 min_beds: parseInt(page?.marketReport?.min_beds ), 3358 min_lot: parseInt(page?.marketReport?.min_lot ), 3359 min_price: parseInt(page?.marketReport?.min_price ), 3360 min_year_built: parseInt(page?.marketReport?.min_year_built), 3361 max_area: parseInt(page?.marketReport?.max_area), 3362 max_baths: parseInt(page?.marketReport?.max_baths), 3363 max_beds: parseInt(page?.marketReport?.max_beds), 3364 max_lot: parseInt(page?.marketReport?.max_lot), 3365 max_price: parseInt(page?.marketReport?.max_price), 3366 max_year_built: parseInt(page?.marketReport?.max_year_built), 3367 name : name, 3368 postal_code: page?.marketReport?.postal_code, 3369 state_abbreviation: page?.marketReport?.state_abbreviation, 3370 street_address: page?.marketReport?.street_address 3371 3372 } 3373 3374 3375 3376 if(page.isNewReport){ 3377 blueroofMiddlewareApi.createMarketReport(newSavedMarketReport).then((data) => { 3378 lifecycle.removeUnsavedChanges('unsavedChanges'); 3379 saveReportModal.hide(); 3380 toast.show('Market Report Saved') 3381 3382 pageSubHeader.hideEditCriteriaButton(); 3383 pageSubHeader.hideSaveReportButton(); 3384 3385 if(data?.search_type == 'area'){ 3386 pageSubHeader.showGetHomeValueButton(); 3387 pageSubHeader.invertButtonStyles("SellMyHome", false); 3388 pageSubHeader.invertButtonStyles("MoreMenuButton", true); 3389 } else { 3390 pageSubHeader.invertButtonStyles("SellMyHome", true); 3391 pageSubHeader.invertButtonStyles("MoreMenuButton", false); 3392 } 3393 3394 handleMenuItemStyles(); 3395 handleScreenWidthSubHeaderButtons(pageSubHeader, data?.search_type == 'area', data?.search_type != 'area'); 3396 3397 page.isReportLoaded = true; 3398 page.isNewReport = false;
3399 page.marketReport = data; 3400 page.reportId = data.id; 3401 3402 }).catch((e) => { 3403 saveReportModal.hide(); 3404 toast.show('Error saving market report', true) 3405 }) 3406 if (nextReportData) { 3407 Pubsub.publish('create_market_report_selected_location', {selectedLocation: nextReportData}); 3408 } 3409 } 3410 else{ 3411 blueroofMiddlewareApi.updateMarketReport(page.reportId, newSavedMarketReport).then((data) => { 3412 lifecycle.removeUnsavedChanges('unsavedChanges'); 3413 saveReportModal.hide(); 3414 toast.show('Market Report Saved') 3415 }).catch((e) => { 3416 saveReportModal.hide(); 3417 toast.show('Error saving market report', true) 3418 }) 3419 } 3420 }).subscribe() 3421 3422 Pubsub.subscription('save_report', (data) => { 3423 Pubsub.publish('save_report_modal_save_clicked', {report_title: page.marketReport.location, report_frequency:'Every 2 Weeks'}, data) 3424 }).subscribe() 3425 3426 Pubsub.subscription('report_frequency_modal_save_clicked', (reportFrequency) => { 3427 3428 let frequency; 3429 3430 //valid options for frequency { never, every_week, every_2_weeks, every_month, quarterly, annually } 3431 switch(reportFrequency){ 3432 case 'Never': 3433 frequency = 'never'; 3434 break; 3435 case 'Every Week': 3436 frequency = 'every_week'; 3437 break; 3438 case 'Every 2 Weeks': 3439 frequency = 'every_2_weeks'; 3440 break; 3441 case 'Every Month': 3442 frequency = 'every_month'; 3443 break; 3444 case 'Quarterly': 3445 frequency = 'quarterly'; 3446 break; 3447 case 'Annually': 3448 frequency = 'annually'; 3449 break; 3450 } 3451 3452 page.marketReport.recurring_frequency = frequency 3453 3454 blueroofMiddlewareApi.updateMarketReport(page.reportId, page.marketReport).then((data) => { 3455 lifecycle.removeUnsavedChanges('unsavedChanges'); 3456 toast.show('Market Report Saved') 3457 frequencyModal.hide(); 3458 }).catch((e) => { 3459 toast.show('Error saving market report', true) 3460 }) 3461 3462 }).subscribe() 3463 3464 Pubsub.subscription('slipstream_is_this_your_home_create', (data) => { 3465 3466 const beds = parseInt(data?.slipStreamData?.beds || 0); 3467 const baths = parseInt(data?.slipStreamData?.baths || 0); 3468 const sqft = parseInt(data?.slipStreamData?.sqft?.toString().replaceAll(',','') || 0); 3469 const yearBuilt = parseInt(data?.slipStreamData?.year_built || 0); 3470 3471 let reportData = data.reportData 3472 3473 if(beds > 0){ 3474 reportData.min_beds = beds - page.bedOffset; 3475 reportData.max_beds = beds + page.bedOffset; 3476 } 3477 if(baths > 0){ 3478 reportData.min_baths = baths - page.bathOffset; 3479 reportData.max_baths = baths + page.bathOffset; 3480 } 3481 if(sqft > 0){ 3482 reportData.min_area = sqft - page.sqftOffset; 3483 reportData.max_area = sqft + page.sqftOffset; 3484 } 3485 3486 loadingModal.show(); 3487 3488 blueroofMiddlewareApi.homeValueReport(reportData).then(report => { 3489 localStorage.setItem('marketReportData', JSON.stringify(report)); 3490 window.location = `${window.origin}/myportal/report?market_report_id=${report.id}` 3491 }).catch((error) => { 3492 loadReport(false, false); 3493 }) 3494 3495 }).subscribe() 3496 3497 Pubsub.subscription('editHomeFactsClicked', (report) => { 3498 if(page.marketReport.search_type == 'area'){ 3499 marketReportModal.show(); 3500 } 3501 else{ 3502 editHomeFactsModal.setHomeFacts(report || page.marketReport); 3503 editHomeFactsModal.show(); 3504 } 3505 }).subscribe(); 3506 3507 Pubsub.subscription('editHomeFactsModalSaved', (data) => { 3508 const { beds, baths, sqft, year_built } = data.changes; 3509 3510 marketReport.homeDetails.setAttribute('beds', parseInt(beds)); 3511 marketReport.homeDetails.setAttribute('baths', parseInt(baths)); 3512 marketReport.homeDetails.setAttribute('sqft', sqft); 3513 marketReport.homeDetails.setAttribute('build_year', parseInt(year_built)); 3514 3515 Pubsub.publish('update_report_details', {'changes':data.slipStreamData, "marketReport" : data.reportData}); 3516 }).subscribe(); 3517 3518 Pubsub.subscription('editNameAndFrequencySaved', (data) => { 3519 Pubsub.publish('update_report_details', {'changes':data.inputData, "marketReport" : data.reportData}); 3520 }).subscribe(); 3521 3522 Pubsub.subscription('previewError', (statusCode) => { 3523 if(statusCode == 410){ 3524 mlsErrorModal.show() 3525 } else if (statusCode == 422){ 3526 mlsErrorMarketReportModal.show() 3527 } 3528 else{ 3529 toast.show('Error building market report preview', true) 3530 } 3531 }).subscribe(); 3532 3533 3534 Pubsub.subscription('get_home_value', () => { 3535 getYourHomeCTA.showGetHomeValueCTA(); 3536 }).subscribe() 3537 3538 Pubsub.subscription('sell_my_home', () => { 3539 let ctaProps = { 3540 first_name : page.lead?.fname || '', 3541 last_name : page.lead?.lname || '', 3542 email_address : page.lead?.email || '', 3543 phone_number : page.lead?.phone || '', 3544 street_address : page.marketReport?.street_address || '', 3545 city : page.marketReport?.city || '', 3546 state : page.marketReport?.state_abbreviation || '', 3547 beds : page.marketReport?.beds || 0, 3548 baths : page.marketReport?.baths || 0, 3549 sqft : page.marketReport?.sqft || 0, 3550 year_built : '' 3551 } 3552 3553 getYourHomeCTA.setData(ctaProps) 3554 getYourHomeCTA.showSellMyHomeCTA(); 3555 3556 }).subscribe() 3557 3558 Pubsub.subscription('contact_us', () => { 3559 let ctaProps = { 3560 first_name : page.lead?.fname || '', 3561 last_name : page.lead?.lname || '', 3562 email_address : page.lead?.email || '', 3563 phone_number : page.lead?.phone || '', 3564 } 3565 3566 getYourHomeCTA.setData(ctaProps) 3567 getYourHomeCTA.showContactUsCTA(); 3568 }).subscribe() 3569 3570 Pubsub.publish('window_resize') 3571 3572 let modalBackdrop = document.getElementsByClassName('center-modal'); 3573 for(let i = 0; i < modalBackdrop.length; i++){ 3574 modalBackdrop[i].addEventListener('click', (e) => { 3575 if(e.target == modalBackdrop[i]){ 3576 modalBackdrop[i].style.display = 'none'; 3577 } 3578 }); 3579 } 3580 3581 document.addEventListener("click", (e) => { 3582 if(emptyMarketReport.element.getAttribute('data') != '[]') { 3583 emptyMarketReport.element.setAttribute('data', '[]'); 3584 } 3585 }); 3586 })(); 3587 } 3588}); 3589 3590 3591</script>
3591 <div id="myportal-page" style="display:none"> <div style=" position:relative; width:100%;";> <div id="drop-menu-backdrop" style="position:absolute; display:none; top:0; bottom:0; right:0; width:100%; height:100%; display:none;z-index:200"></div> <div style="position:absolute;z-index:110; top:0; bottom:0; "> <place-menu></place-menu> </div> <div id="menu-backdrop" style="position:absolute; display:none; top:0; bottom:0; right:0; width:100%; height:100%; display:none;z-index:205"></div> <div style="position:absolute;z-index:210; top:0; bottom:0; right:0"> <place-marketreportmodal id="market-report-modal"> </place-marketreportmodal> </div> <div class="collection-page" style="flex:1;"> <div class="page-width-parent"> <place-pagesubheader title="MARKET REPORTS" top_header_title="Market Reports" show_top_header="false" show_button="false" ></place-pagesubheader> <div id="saved-report-section"> <place-emptymarketreport image="https://images.brivityidx.com/assets/images/mobile-library-images/empty_house.png" style="display:none" data="[]"></place-emptymarketreport> <div id="report-section" style="display:flex; flex-wrap:wrap; margin-top:24px;" > <place-createmarketreport style="display:none;z-index:100;" data="[]"> </place-createmarketreport> </div> <place-pagination id="reports-pagination" style="display:none"></place-pagination> <div id="gethomeapp-parent" style="margin:0 0 32px"> <place-gethomeappreports id="reportListingViewGetHomeApp" container_styles="bg-[#F9F9F9]"></place-gethomeappreports> </div> </div> <div id="view-report-section" class="report" style="display:none"> <div class="top-element-container"> <place-realtorcontactcta container_styles="border-[#E8E8E8] border-1 rounded-[4px]" image_url="" name="" phone="" email="" > </place-realtorcontactcta> </div> <div id="estimatedHomeValue" class="report-section-container"> <place-estimatedhomevalue v2="true" content_styles="self-center" container_styles="w-full flex flex-col"></place-estimatedhomevalue> </div> <div id="homeDetails" class="report-section-container"> <place-homedetails></place-homedetails> </div> <div id="homeequitycalculator" class="report-section-container"> <place-homeequitycalculator></place-homeequitycalculator> </div> <div class="report-section-container"> <place-instantoffercard id="useEquityCTA" title="Ready to Use Your Equity?" description="Whether you are in the market for a sale, an upgrade, or a refinance, let's talk about your next steps." button_text="Contact Agent" > </place-instantoffercard> </div> <div class="report-section-container"> <place-markettemperatureblock v2="true" container_styles="px-6 border-[#E8E8E8] border-1 rounded-[4px]"> </place-markettemperatureblock> </div> <div class="report-section-container"> <place-doesmyhomecompare v2="true" container_styles="border-[#E8E8E8] border-1 rounded-[4px]" ></place-doesmyhomecompare> </div> <div class="report-section-container"> <place-markettrends container_styles="rounded-[4px] border-[#E8E8E8] border-1" items='[{ "icon": "zap", "label": "New Homes", "value": "0"},{ "icon": "tag", "label": "Sold", "value": "1"}, { "icon": "dollar-sign", "label": "Average", "value": ""}, { "icon": "calendar", "label": "Days On Market", "value": "3"}]'> </place-markettrends> </div> <div class="report-section-container"> <place-instantoffercard id="competitiveSellCTA" title="Want to Be More Competitive When You Sell?" description="Minor home improvements can make a big difference for a buyer. Connect with our team to explore impactful improvements designed to highlight your home's unique features." button_text="Learn More" > </place-instantoffercard> </div> <div class="report-section-container" id="map"> <place-mapblock google_api_key=AIzaSyAjpuNg6WGCMV5rzoJcIP2QmMTVto6hYmc show_sold_price="true" > </place-mapblock> </div> <div id="sendMessage" class="report-section-container"> <place-sendmessage v2="true" container_styles="border-[#E8E8E8] border-1 rounded-[4px]" ></place-sendmessage> </div> <div id="ownAnotherHome" class="report-section-container"> <place-ownanotherhome container_styles="border-[#E8E8E8] border-1 rounded-[4px]"></place-ownanotherhome> </div> <div id="getHomeApp" class="report-section-container"> <place-gethomeapp id="reportViewGetHomeApp" container_styles="rounded-[3px]" column_view="true" v2="true" long_text="true"></place-gethomeapp> </div> </div> </div> </div> </div> <place-getyourhomecta class="center-modal" style="display:none" interest_items='[{"text":"Looking to sell ASAP"},{"text":"Looking to sell (1-3 months)"},{"text":"Looking to sell (3-6 months)"}, {"text":"Looking to sell (6-12 months)"},{"text":"Looking to sell (12+ months)"}, {"text":"Just curious"}]' > </place-getyourhomecta> <place-basicmodal id="delete-market-report-modal" class="center-modal" style="display:none" title="Delete Market Report" sub_title="Deleting this market report will permanently remove it from your account. Are you sure you would like to delete this report?" title_icon="trash" primary_button_text="Delete" tertiary_button_text="Cancel" ></place-basicmodal> <place-basicmodal id="mls-error-modal" class="center-modal" style="display:none" title="Home Value Estimate" sub_title="" title_icon="" primary_button_text="Contact Agent" tertiary_button_text="Close" ></place-basicmodal> <place-basicmodal id="mls-error-market-report-modal" class="center-modal" style="display:none" title="Market Report Unavailable" sub_title="" title_icon="" primary_button_text="Try Again" ></place-basicmodal> <place-basicmodal container_style="w-[412px] h-[292px] rounded-[1px]" id="verify-home-estimate-modal" class="center-modal" style="display:none" title="Verify My Home Estimate" sub_title="Your agent will be notified and will reach out shortly to verify your home estimate!" title_icon="" primary_button_text="Verify My Home Estimate" tertiary_button_text="Cancel" stacked_buttons="true" ></place-basicmodal> <place-loadingmodal title="Hang tight while we build your report" image="https://cdn1.brivityidx.com/assets/images/mobile-library-images/loadinghouse.gif" sub_title="" class="center-modal" style="display:none"> </place-loadingmodal> <place-useyourequitymodal class="center-modal" style="display:none" ></place-useyourequitymodal> <place-isthisyourhome class="center-modal" style="display:none"></place-isthisyourhome> <place-savereportpopup loading="false" report_name="" class="center-modal" style="display:none" > </place-savereportpopup> <place-updatereportpopup loading="false" report_name="" class="center-modal" style="display:none;" > </place-updatereportpopup> <place-frequencymodal class="center-modal" style="display:none" title="Market Report Frequency" title_icon="clock" menu_items='[{ "text": "Every Week" },{ "text": "Every 2 Weeks" },{ "text": "Every Month" },{ "text": "Quarterly" },{ "text": "Annually" },{ "text": "Never" }]' selected_menu_item="Never" > </place-frequencymodal> <place-edithomefactsmodal class="center-modal" style="display:none" home_facts='{"beds": 3, "baths": 2, "sqft": 1500}' v2="true" ></place-edithomefactsmodal> <place-otherestimates class="center-modal" style="display:none" ></place-otherestimates> </div> <footer id="footer-8n" style="padding: 100px 0px; background-color: rgb(255, 255, 255) !important; outline: none; cursor: inherit;" data-selector="footer"><div class="container"> <div class="row"> <div class="col-sm-4"> <div class="footer-logo-container"> <img class="img-responsive" src="https://images.brivityidx.com/assets/images/uploads/7586/logo-01.png" alt="" data-selector="img" style="border-radius: 0px !important; border-color: rgb(128, 128, 128) !important; border-style: none !important; border-width: 0px !important; height: 70px !important; width: 200px !important; outline: none; cursor: inherit;"></div> <div> <p data-selector="p" style="outline: none; cursor: inherit;"><span class="agent_address_dynamic dynamic_loading dynamic_loading dynamic_loading dynamic_loading dynamic_loading">7455 McVay Station Ct</span><br><span class="agent_city_dynamic dynamic_loading dynamic_loading dynamic_loading dynamic_loading dynamic_loading">Germantown</span>, <span class="agent_state_dynamic dynamic_loading dynamic_loading dynamic_loading dynamic_loading dynamic_loading">TN</span> <span class="agent_zip_dynamic dynamic_loading dynamic_loading dynamic_loading dynamic_loading dynamic_loading">38138</span></p> <p data-selector="p" style="outline: none; cursor: inherit;"><a class="agent_phone_dynamic dynamic_loading dynamic_loading dynamic_loading dynamic_loading dynamic_loading" href="tel:9016255200" src="tel:" style="outline: none; cursor: inherit;">(901) 625-5200</a><br> </p></div> </div> <div class="col-sm-3"> <hr data-selector="hr" style="outline: none; cursor: inherit;"><h3 class="ediContent" data-selector="h1, h2, h3, h4, h5, h6" style="">Quick Links</h3> <ul class="list-unstyled editContent" style="" data-selector=".editContent"><li data-selector="li" style="outline: none; cursor: inherit;"><a href="/home_value" style="outline: none; cursor: inherit;">See Home Value</a></li> <li data-selector="li" style="outline: none; cursor: inherit;"><a id="top-nav-marketing-plan" data-toggle="modal" data-target="#sell-modal" style="outline: none; cursor: inherit;">Get Marketing Plan</a></li> </ul></div> <div class="col-sm-5"> <hr data-selector="hr" style="outline: none; cursor: inherit;"><h3 class="editContent" data-selector=".editContent" style="outline: none; cursor: inherit;">More</h3> <ul class="list-unstyled" style="outline: none; cursor: inherit;"><li data-selector="li" style="outline: none; cursor: inherit;"><a href="/index" style="outline: none; cursor: inherit;">Home </a></li> <li data-selector="li" style="outline: none; cursor: inherit;"><a href="/why_list_with_us" style="outline: none; cursor: inherit;">Why List with Us?</a></li> </ul></div> <div class="col-sm-5"> <hr data-selector="hr" style="outline: none; cursor: inherit;"><h3 class="editContent" data-selector=".editContent" style="">OUR LOCATIONS</h3> <ul class="list-unstyled" style=""><li data-selector="li" style="outline: none; cursor: inherit;"><a href="/index" style="">Germantown, Collierville, Arlington, Lakeland, Cordova, Bartlett, Memphis </a></li> </ul></div> </div> <div class="row margin-top-40"> <div class="col-sm-4 anchor-col"> <ul class="list-inline social" style="outline: none; cursor: inherit;"><li data-selector="li" style="outline: none; cursor: inherit;"> <a class="agent_facebook_dynamic dynamic_loading dynamic_loading dynamic_loading dynamic_loading dynamic_loading" href="https://www.facebook.com/livelovememphis/" target="_blank" style="outline: none; cursor: inherit;"> <svg viewbox="0 0 24 24" width="24" height="24" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round" class="css-i6dzq1"><path d="M18 2h-3a5 5 0 0 0-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 0 1 1-1h3z"></path></svg></a> </li> <li data-selector="li" style="outline: none; cursor: inherit;"> <a class="agent_twitter_dynamic dynamic_loading dynamic_loading dynamic_loading dynamic_loading dynamic_loading" href="https://twitter.com/livelovememphis?lang=en" target="_blank" style="outline: none; cursor: inherit;"> <svg viewbox="0 0 24 24" width="24" height="24" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round" class="css-i6dzq1"><path d="M23 3a10.9 10.9 0 0 1-3.14 1.53 4.48 4.48 0 0 0-7.86 3v1A10.66 10.66 0 0 1 3 4s-4 9 5 13a11.64 11.64 0 0 1-7 2c9 5 20 0 20-11.5a4.5 4.5 0 0 0-.08-.83A7.72 7.72 0 0 0 23 3z"></path></svg></a> </li> <li data-selector="li" style="outline: none; cursor: inherit;"> <a class="agent_instagram_dynamic dynamic_loading dynamic_loading dynamic_loading dynamic_loading dynamic_loading" href="https://www.instagram.com/livelovememphis/?hl=en" target="_blank" style="outline: none; cursor: inherit;"> <svg viewbox="0 0 24 24" width="24" height="24" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round" class="css-i6dzq1"><rect x="2" y="2" width="20" height="20" rx="5" ry="5"></rect><path d="M16 11.37A4 4 0 1 1 12.63 8 4 4 0 0 1 16 11.37z"></path><line x1="17.5" y1="6.5" x2="17.5" y2="6.5"></line></svg></a> </li> </ul></div> <div class="col-sm-8 anchor-col"> <p class="margin-bottom-0" data-selector="p" style="outline: none; cursor: inherit;"><span class="get_this_year"></span> © The Live Love Memphis Group | Real Estate Agency. <span>All rights reserved.</span></p> <p style="margin: 10px 0px; outline: none; cursor: inherit;" data-selector="p"> <img src="https://images.brivityidx.com/assets/images/uploads/1428/Equal-Housing-Realtor_gray50.png" alt="Equal Housing | Realtor" style="width: 50px; outline: none; cursor: inherit;" data-selector="img">  Each office is independently owned and operated. </p> </div> </div> </div> </footer> </div> <section id="seo-anchor"> <div class="container"> <ul class="powered-by"> <li>Powered by <a class="pb-vendor" href="https://www.brivity.com/"> Brivity</a></li> <li class="pb-admin-login"><a href="https://login.brivityidx.com/login"><span class='pb-admin-login-text'>Admin Log In</span></a></li> </ul> <ul class="seo-anchor-links"> <li><a href="/privacy_policy">Privacy Policy</a></li> <li><a href="/tos">DMCA & Terms of Service</a></li> <li><a id='sitemapUrl' href='/sitemap' style='display: inline;'>Sitemap</a></li> </ul> </div> </section> <input type="hidden" id="hchatbot_settings" data-chatbot-version="1" value="{"chat_position":"right","call_to_action_text":"Hi there! Please let us know if you have any questions and an agent will reach out to you as soon as possible.","initial_message":"Enter your question below and an agent will be in touch with you.","tags":"IDX Chat","version":"1"}"> 3592<!-- chatbot v1 --> 3593<!-- Chatbot drawer container --> 3594<div class="chatbot-drawer-container loading"> 3595 <div class="chatbot-content-wrapper"> 3596 <div class="chatbot-outside-content"> 3597 <div class="chatbot-bubble-message-container cb-hide"> 3598 <img src="https://cdn.brivity.com/images/people/16717093/54cd6c6a08d4cae8e13641572de36ac9f72adcfa-original.jpg?1755537591" class="chatbot-agent-img" alt="Agent profile image in chat bubble"/> 3599 <div id="call-to-action" class="bubble-message"></div> 3600 <button type="button" class="chatbox-bubble-close" data-dismiss="modal" aria-label="Close">
3601 <span aria-hidden="true">×</span> 3602 </button> 3603 </div> 3604 <button id="chatbot-open-btn" class="chatbot-open-btn bg-prmry" aria-label="Chatbot Open Button"> 3605 <svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg"> 3606 <path d="M20.8 6.35501V16C20.8 17.765 19.365 19.195 17.6 19.195L11.2 19.2L5.755 22.28C5.645 22.365 5.52 22.4 5.4 22.4C5.09 22.4 4.8 22.155 4.8 21.8V19.2H3.2C1.4375 19.2 0 17.765 0 16V6.35501C0 4.63751 1.4375 3.20001 3.2 3.20001H17.6C19.365 3.20001 20.8 4.63751 20.8 6.35501ZM7.2 18.705L10.57 16.8H17.6C18.035 16.8 18.4 16.44 18.4 16V6.40001C18.4 5.96251 18.035 5.60001 17.6 5.60001H3.2C2.7625 5.60001 2.4 5.96251 2.4 6.40001V16C2.4 16.44 2.7625 16.8 3.2 16.8H7.2V18.705ZM12.8 22.4V20.8H15.2V22.4C15.2 22.835 15.56 23.2 16 23.2H21.43L24.8 25.105V23.2H28.8C29.235 23.2 29.6 22.835 29.6 22.4V12.8C29.6 12.365 29.235 12 28.8 12H22.4V9.60001H28.8C30.565 9.60001 32 11.035 32 12.755V22.4C32 24.165 30.565 25.6 28.8 25.6H27.2V28.2C27.2 28.555 26.91 28.8 26.6 28.8C26.48 28.8 26.355 28.765 26.245 28.68L20.8 25.6L16 25.595C14.24 25.595 12.8 24.165 12.8 22.4Z" fill="white"/> 3607 </svg> 3608 </button> 3609 </div> 3610 <div class="chatbot-drawer"> 3611 <div class="chatbot-header bg-prmry"> 3612 <div class="chatbot-agent-info"> 3613 <img src="https://cdn.brivity.com/images/people/16717093/54cd6c6a08d4cae8e13641572de36ac9f72adcfa-original.jpg?1755537591" class="chatbot-agent-img" alt="Agent profile image in chat header"/> 3614 <div class="chatbot-agent"> 3615 <h3 class="chatbot-agent-name">Jen Carstensen</h3> 3616 <h4><span class="chatbot-agent-title">Broker/Owner</span> | <span class="chatbot-agent-company">Real Estate Agency</span></h4> 3617 </div> 3618 <a id="chatbot-agent-phone" style="display: none;">(901) 201-0280</a> 3619 </div> 3620 <button id="close-chatbot-drawer" type="button" class="chatbox-modal-close" data-dismiss="modal" aria-label="Close"> 3621 <span aria-hidden="true">×</span> 3622 </button> 3623 </div> 3624 <div class="chatbot-body"> 3625 <div class="chatbot-message-container"> 3626 <img src="https://cdn.brivity.com/images/people/16717093/54cd6c6a08d4cae8e13641572de36ac9f72adcfa-original.jpg?1755537591" class="chatbot-agent-img" alt="Agent profile image in message"/> 3627 <h4 id="initial_message" class="chatbot-chat-messages not-loaded"></h4> 3628 <div class="chatbot-chat-messages loading"> 3629 <span class="typing-indicator"> 3630 <span></span> 3631 <span></span> 3632 <span></span> 3633 </span> 3634 </div> 3635 </div> 3636 <form id="chatbotForm" class="chatbot-form"> 3637 <input type="name" id="chatbot_fullname" class="chatbox-form-control no-native-focus" placeholder="Full Name" 3638 pattern="^[A-Za-z'\- ]+ [A-Za-z'\- ]+$" 3639 required /> 3640 <input type="tel" id="chatbot_phone" class="chatbox-form-control no-native-focus" placeholder="Phone" required /> 3641 <input type="email" id="chatbot_email" class="chatbox-form-control no-native-focus" placeholder="Email" 3642 pattern="^[^@\s]+@[^@\s]+\.[^@\s]+$" 3643 required /> 3644 <textarea id="chatbot_message" class="chatbox-form-control chatbot-message" rows="5" name="notes" placeholder="Any Comments or Questions"></textarea> 3645 <div class="chatbot-consent-container"> 3646 <div class="chatbot-toggle"> 3647 By proceeding and clicking the sign up button, you consent to receive calls and texts (SMS/MMS) including marketing and promotional communications, from <span class="agent_name_dynamic"></span>, its affiliates, and agents or vendors acting on their behalf. These
3647communications may be delivered using an automatic telephone dialing system or by using an artificial or prerecorded voice. Message and data rates may apply. To opt out of text messages, reply STOP. Your consent is not required as a condition of any purchase. By continuing, you also agree to our <a class="opt-out-href" href="/tos">Terms of Use</a> and <a class="opt-out-href" href="/privacy_policy">Privacy Policy</a>. 3648 </div> 3649 </div> 3650 <input type="hidden" id="chatbot_tags"> 3651 </form> 3652 </div> 3653 <div class="chatbot-footer"> 3654 <button type="submit" form="chatbotForm" class="chatbot-send bg-prmry chatbot-btn-disabled" data-dismiss="modal" aria-label="Close" disabled> 3655 Send 3656 <span aria-hidden="true"> 3657 <svg class="chatbot-btn-icon" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"> 3658 <g clip-path="url(#clip0_2435_997)"> 3659 <path d="M14.6667 1.33331L7.33337 8.66665" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> 3660 <path d="M14.6667 1.33331L10 14.6666L7.33337 8.66665L1.33337 5.99998L14.6667 1.33331Z" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> 3661 </g> 3662 <defs> 3663 <clipPath id="clip0_2435_997"> 3664 <rect width="16" height="16" fill="white"/> 3665 </clipPath> 3666 </defs> 3667 </svg> 3668 </span> 3669 </button> 3670 </div> 3671 </div> 3672 </div> 3673</div> <style> 3674 .signup-consent-checkbox-section { 3675 display: none; 3676 flex-direction:column; 3677 align-items:flex-start; 3678 padding: 6px 0; 3679 } 3680 3681 .signup-consent-checkbox-section .checkbox { 3682 display:flex; 3683 align-items:flex-start; 3684 justify-content:center; 3685 gap:8px; 3686 padding:0 !important; 3687 } 3688 3689 .signup-consent-checkbox-section .checkbox input { 3690 position:unset; 3691 margin:2px 0 0 0 !important; 3692 } 3693 3694 .signup-consent-checkbox-section .checkbox span { 3695 color: #666666; 3696 font-size: 14px; 3697 font-weight: 400; 3698 line-height:16px; 3699 } 3700 3701 .signup-verbage { 3702 text-align:center; 3703 font-size:10px; 3704 line-height:16px; 3705 } 3706 3707 .signup-verbage span { 3708 color: #8C8C8C; 3709 font-weight:600; 3710 } 3711 3712 .signup-verbage a { 3713 color:#5386E4 !important; 3714 font-weight:400; 3715 } 3716 3717 .signup-verbage .twilio-enabled-verbage { 3718 display:none; 3719 } 3720 3721 3722 3723 </style> <div id="modal-mount" data-modal-bundle="https://cdn1.brivityidx.com/assets/modals/v1/all.html?ck=1790798866"></div>
3723 <div id="credential_picker_container_centered"> <div id="credential_picker_header_bar"> <p><button class="signOrLogBtn" onclick="showSignupModal();">Sign up</button> or <button class="signOrLogBtn" onclick="showLoginModal();">Login</button> another way</p> </div> <div id="credential_picker_header_bar_separator"></div> </div> <place-getyourhomecta id="contact_modal" style="display:none; justify-content: center; align-items: center; position:fixed; background-color:rgba(0,0,0,0.4); left:0; top:0; right:0; bottom:0; z-index: 100;" interest_items='[{"text":"Looking to sell ASAP"},{"text":"Looking to sell (1-3 months)"},{"text":"Looking to sell (3-6 months)"}, {"text":"Looking to sell (6-12 months)"},{"text":"Looking to sell (12+ months)"}, {"text":"Just curious"}]' > </place-getyourhomecta> <place-generictoast appear_from_both="true" container_style="right-1/2 z-200"></place-generictoast>
3723<script> 3724 document.addEventListener("DOMContentLoaded", () => { 3725 class Toast { 3726 constructor() { 3727 this.element = document.querySelector('place-generictoast'); 3728 } 3729 3730 show(message, error = false, link) { 3731 var dispatch = function () { 3732 window.dispatchEvent( 3733 new CustomEvent("ShowToast", { 3734 detail: { 3735 text: message, 3736 error: error, 3737 link: link, 3738 }, 3739 }) 3740 ); 3741 }; 3742 if (window.sitebuilderPlaceComponents && window.sitebuilderPlaceComponents.ensureLoaded) { 3743 window.sitebuilderPlaceComponents 3744 .ensureLoaded('place-generictoast') 3745 .then(dispatch) 3746 .catch(function (err) { 3747 console.warn('toast.show fallback (web-components unavailable):', message, err); 3748 }); 3749 } else { 3750 dispatch(); 3751 } 3752 } 3753 } 3754 3755 window.toast = new Toast(); 3756 }); 3757 </script>
3757
3757<script src="https://cdn.jsdelivr.net/npm/@turf/[email protected]/turf.min.js"></script>
3757
3757<script src="https://maps.googleapis.com/maps/api/js?libraries=geocoding,places,marker,drawing&key=AIzaSyAjpuNg6WGCMV5rzoJcIP2QmMTVto6hYmc&map_ids=85c127b7bf0255d9"></script>
3757
3757<script src="https://cdn1.brivityidx.com/assets/js/build.min.js?ck="></script>
3757
3757<script src="https://cdn1.brivityidx.com/assets/js/top_bar_v2.min.js?ck="></script>
3757
3757<script src="https://cdn1.brivityidx.com/assets/js/global_js.min.js?ck=1790798865"></script>
3757
3757<script>deferScript("https://cdn1.brivityidx.com/assets/js/global_js_defer.min.js?ck=1790798865", 5000);</script>
3757
3757<script> 3758 function shouldLoadChatbotImmediately() { 3759 const settingsEl = document.getElementById('hchatbot_settings'); 3760 if (!settingsEl) return false; 3761 3762 const version = Number(settingsEl.dataset.chatbotVersion); 3763 let isInitialized = false; 3764 3765 try { 3766 const session = JSON.parse(sessionStorage.getItem('cbSession') || '{}'); 3767 isInitialized = session.chatbotInitialized === true; 3768 } catch (e) {} 3769 3770 return version > 1 && isInitialized; 3771 } 3772 </script>
3772
3772<script>document.getElementById('hchatbot_settings') && Number(document.getElementById('hchatbot_settings').dataset.chatbotVersion) > 1 && deferScript("https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/9.0.6/signalr.min.js", 0);</script>
3772
3772<script>document.getElementById('hchatbot_settings') && deferScript(Number(document.getElementById('hchatbot_settings').dataset.chatbotVersion) > 1 ? "https://cdn1.brivityidx.com/assets/js/chatbot_v2.min.js?ck=1790798864" : "https://cdn1.brivityidx.com/assets/js/chatbot.min.js?ck=1790798864", shouldLoadChatbotImmediately() ? 50 : 5000);</script>
3772
3772<script type="text/javascript" src="https://cdn1.brivityidx.com/assets/js/jquery.geocomplete.min.js"></script>
3772<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery.bootstrapvalidator/0.5.3/js/bootstrapValidator.min.js"></script>
3772<script type="text/javascript" src="https://cdn1.brivityidx.com/assets/js/member_area_components.js"></script>
3772<script type="text/javascript" src="https://cdn.jsdelivr.net/gh/GoogleChromeLabs/[email protected]/dist/lifecycle.es5.js"></script>
3772<script type="text/javascript" src="https://cdn1.brivityidx.com/assets/js/update_agent_info.js"></script>
3772
3772<script src="https://mobile-component-services-library.s3.amazonaws.com/messaging_service.min.js?ck=1"></script>
vendor: 68 bytes, line 3772
3772 <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=
3772GTM-K2WGMDL
vendor: 83 bytes, line 3772
3772" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
3772<script>$(document).ready(function(){$("ul.seo-anchor-links li:nth-of-type(4) a").attr("href","/search")})</script>
3772
3772<script>$(document).ready(function(){$("#sitemap-listings > div.container > div > div.col-xs-12.col-md-12.col-lg-4.col-lg-push-8.seo-intro > a").attr("href","/search")})</script>
3772</body> </html>
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.