← back to Malden House

browse.js

75 lines

// browse.js — public rental directory, card grid from localStorage
(function () {
  function load() {
    try { return JSON.parse(localStorage.getItem("htg.rentals") || "{}"); }
    catch (e) { return {}; }
  }
  function escapeHtml(s) {
    return String(s ?? "")
      .replace(/&/g, "&")
      .replace(/</g, "&lt;")
      .replace(/>/g, "&gt;")
      .replace(/"/g, "&quot;")
      .replace(/'/g, "&#39;");
  }
  const escapeAttr = escapeHtml;
  function formatMoney(v) {
    return v === null || v === undefined || v === "" ? "—" : escapeHtml(v.toLocaleString());
  }
  const all = Object.values(load());
  const venues = [...new Set(all.flatMap(r => (r.nearest || []).slice(0,1).map(v => v.name)))].sort();
  const sel = document.getElementById("br-venue");
  venues.forEach(v => {
    const o = document.createElement("option");
    o.value = v; o.textContent = v.replace(/· .*$/, "").trim();
    sel.appendChild(o);
  });

  const budget = document.getElementById("br-budget");
  const bv = document.getElementById("br-bv");
  budget.addEventListener("input", () => { bv.textContent = "$" + (+budget.value).toLocaleString(); render(); });
  sel.addEventListener("change", render);

  function render() {
    const maxP = +budget.value;
    const venueFilter = sel.value;
    const rows = all.filter(r => {
      const n0 = (r.nearest || [])[0];
      if (!n0) return false;
      if (r.price && r.price.mid > maxP) return false;
      if (venueFilter && n0.name !== venueFilter) return false;
      return true;
    });
    const grid = document.getElementById("br-grid");
    const empty = document.getElementById("br-empty");
    if (!rows.length) { grid.innerHTML = ""; empty.hidden = false; return; }
    empty.hidden = true;
    grid.innerHTML = rows.map(r => {
      const n0 = r.nearest[0];
      const photo = (r.photos && r.photos[0]) || null;
      const street = r.address.street || "";
      const cityLine = [r.address.city, r.address.state].filter(Boolean).join(", ");
      const chip = `${n0.mi.toFixed(1)} mi · ${n0.headline || (n0.sports || "").split("·")[0]}`;
      const sat = `https://maps.google.com/maps?q=${encodeURIComponent(`${r.address.lat},${r.address.lng}`)}&t=k&z=18&output=embed`;
      return `
        <a class="br-card" href="rental.html?id=${encodeURIComponent(r.slug)}">
          <div class="br-img">
            ${photo
              ? `<img src="${escapeAttr(photo)}" alt="${escapeAttr(street)}">`
              : `<iframe src="${escapeAttr(sat)}" loading="lazy"></iframe>`}
            <span class="br-chip">${escapeHtml(chip)}</span>
          </div>
          <div class="br-body">
            <div class="br-addr">${escapeHtml(street)}</div>
            <div class="br-city">${escapeHtml(cityLine)}</div>
            <div class="br-row">
              <span><b>$${formatMoney(r.price && r.price.mid)}</b><em>/ night</em></span>
              <span class="br-host">by ${escapeHtml(r.host)}</span>
            </div>
          </div>
        </a>`;
    }).join("");
  }
  render();
})();