/* ============================================================ app.js — Global state and page initialisation ============================================================ */ const App = { openaiKey: '', // set to '__server__' by init() if configured searchMode: 'landed', // 'landed' | 'nonlanded' transactions: [], // records from current filtered search filtered: [], // after filters applied allNonLandedTx: [], // non-landed set from most recent search (may be year-filtered) allNonLandedTxFull: null, // full 5-year non-landed dataset, used by price trend charts currentTx: null, // transaction currently open in detail panel page: 0, PAGE_SIZE: 100, sgIndex: null, // cadastral spatial index sgIndexState: 'idle', // 'idle' | 'loading' | 'loaded' | 'unavailable' serverReady: false, }; /* ── Panel switching ─────────────────────────────────────────────────────── */ function showPanel(name, fromPopState = false){ // Safety net: if the map fullscreen overlay is active, exit it first so the // browser's scroll is restored before we try to navigate to another panel. if(typeof _mapFs !== 'undefined' && _mapFs && typeof toggleMapFullscreen === 'function'){ toggleMapFullscreen(); } document.getElementById('panel-browser').style.display = name === 'browser' ? '' : 'none'; document.getElementById('panel-resolver').style.display = name === 'resolver' ? '' : 'none'; document.getElementById('nav-browser').classList.toggle('active', name === 'browser'); document.getElementById('nav-resolver').classList.toggle('active', name === 'resolver'); // Always scroll to top — critical on mobile where user is scrolled into the table window.scrollTo({ top: 0, behavior: 'smooth' }); // Leaflet needs an invalidateSize call if its container was hidden if(name === 'browser' && typeof _bMap !== 'undefined' && _bMap){ setTimeout(() => _bMap.invalidateSize(), 60); } if(!fromPopState){ history.pushState({panel: name}, '', '#' + name); } } window.addEventListener('popstate', function(e){ const panel = (e.state && e.state.panel) ? e.state.panel : 'browser'; showPanel(panel, true); }); /* ── Boot sequence ───────────────────────────────────────────────────────── */ (async function init(){ // Honour a URL hash on first load (e.g. index.html#resolver from the // Continuum listing page back-nav). Defaults to 'browser' for bare URLs. const initialHash = (location.hash || '').replace(/^#/, ''); const initialPanel = (initialHash === 'resolver') ? 'resolver' : 'browser'; if(initialPanel !== 'browser'){ // Use the public panel switcher so CSS classes and history state stay // consistent with normal navigation. showPanel(initialPanel, true); } // Stamp initial history entry so back-button navigates within the SPA history.replaceState({panel: initialPanel}, '', location.href); // Load geocoded street/project coordinates for the hero map (non-blocking) loadGeoLookup(); try { const cfg = await fetchConfigStatus(); App.serverReady = cfg.uraReady; App.openaiKey = cfg.openaiReady ? '__server__' : ''; if(!cfg.uraReady){ document.getElementById('browser-output').innerHTML = `
config.json, set your ura_access_key,
then restart server.py.
server.py is running, then refresh the page.