SPB Git forge

spb/house-ka

Public
18commits 1branches 0releases
1.9 MBsize
maindefault branch
19 days agolast push
Python 67% TypeScript 18.2% CSS 14.4%
2.5 KB · 58 lines tsx
Raw Blame History
1// -----------------------------------------------------------------------------2// House-Ka — Homes-for-sale aggregator (Canada outside Québec, Ontario first)3// Author: Simon-Pierre Boucher — contact@spboucher.ai4// components/ListingCard.tsx : property card (results grid)5// -----------------------------------------------------------------------------6import { Link } from "react-router-dom";7import { Listing, fmtArea, fmtPrice, sourceName } from "../api";8import { Ico } from "./Icons";9import PropertyImg from "./PropertyImg";1011export default function ListingCard({ l }: { l: Listing }) {12  // light thumbnail when the connector provides one (mobile/cellular),13  // otherwise the full-size cover photo14  const img = (typeof l.details?.cover_thumb === "string" && l.details.cover_thumb)15    || (l.images && l.images.length > 0 ? l.images[0] : null);16  const rent = l.details?.transaction === "location";17  const meta: { ico: string; txt: string }[] = [];18  if (l.bedrooms != null) meta.push({ ico: "bed", txt: `${l.bedrooms} bed` });19  if (l.bathrooms != null) meta.push({ ico: "bath", txt: `${l.bathrooms} bath` });20  const area = fmtArea(l.area_sqft);21  if (area) meta.push({ ico: "area", txt: area });2223  return (24    <Link to={`/property/${encodeURIComponent(l.uid)}`} className="card">25      <div className="card-img">26        <PropertyImg src={img} alt={l.title || l.address} type={l.property_type} />27        {l.property_type && <span className="badge type">{l.property_type}</span>}28        {l.images.length > 1 && (29          <span className="badge right"><Ico name="camera" size={12} /> {l.images.length}</span>30        )}31      </div>32      <div className="card-body">33        <div className="card-price">34          {fmtPrice(l.price, l.price_label)}35          {rent && <span className="per-month"> /month</span>}36        </div>37        <div className="card-title">{l.address || l.title}</div>38        <div className="card-meta">39          {l.sector && <span>{l.sector}</span>}40          {l.sector && l.city && <span className="sep" />}41          {l.city && <span>{l.city}</span>}42        </div>43        {meta.length > 0 && (44          <div className="card-specs">45            {meta.map((m) => (46              <span key={m.ico}><Ico name={m.ico} size={13} /> {m.txt}</span>47            ))}48          </div>49        )}50        <div className="card-foot">51          <span className="source-tag">{sourceName(l.source)}</span>52          {l.mls && <span className="avail">MLS® {l.mls}</span>}53        </div>54      </div>55    </Link>56  );57}58