/* eslint-disable no-undef */ /* TxnTable.jsx — filter bar + property transaction table for the Malaysia Property Transaction Map tab. Renders after a search has loaded rows at district, mukim, scheme, or road level; handles loading, empty (no records) and filtered-empty states. Filters: Year (single or range), Transaction Price (RM min/max), Property Type (values sourced from the loaded data, not hardcoded). */ const { useState: useStateTT } = React; const numFmt = (n) => n == null ? '—' : n.toLocaleString('en-MY'); const TH = ({ children, right }) => ( {children} ); const TD = ({ children, right, mono, strong }) => ( {children} ); const PriceInput = ({ value, onChange, placeholder }) => (
RM onChange(e.target.value.replace(/[^0-9]/g, ''))} placeholder={placeholder} inputMode="numeric" style={{ border: 0, background: 'transparent', outline: 'none', width: '100%', fontFamily: "'JetBrains Mono',monospace", fontSize: 13, color: C.deep, }}/>
); const TxnTable = ({ sel, loading, txns, filtered, availTypes, types, setTypes, yearMode, setYearMode, yr, setYr, price, setPrice, years, median, fill, }) => { // before any search is run, the bottom sheet can stay hidden. if (!txns && !loading) return null; const toggleType = (t) => setTypes( types.includes(t) ? types.filter(x => x !== t) : [...types, t] ); const filtersActive = types.length !== availTypes.length || yr.single || yr.from || yr.to || price.min || price.max; return ( {/* header strip */}
Property Transactions
{sel.road ? `${sel.road} · ${sel.area} · ${sel.mukim} · ${sel.district} · ${sel.state}` : ''}
{txns && txns.length > 0 && (
Records {filtered.length} / {txns.length}
Median Price {median != null ? formatRM(median) : '—'}
)}
{/* filters */} {txns && txns.length > 0 && !loading && (
{/* Year */}
Year {['single', 'range'].map(m => ( ))} {yearMode === 'single' ? setYr({ ...yr, single: v })} placeholder="Any year" options={years}/> :
setYr({ ...yr, from: v })} placeholder="From" options={years}/> setYr({ ...yr, to: v })} placeholder="To" options={years}/>
}
{/* Price */}
Transaction Price
setPrice({ ...price, min: v })} placeholder="Min"/> setPrice({ ...price, max: v })} placeholder="Max"/>
{/* Property Type */}
Property Type
{availTypes.map(t => ( toggleType(t)}>{t} ))} {filtersActive && ( )}
)} {/* body */} {loading ? (
Loading property transactions…
) : (txns && txns.length === 0) ? ( ) : (filtered.length === 0) ? ( ) : (
{filtered.map((r, i) => ( e.currentTarget.style.background = C.cream} onMouseLeave={e => e.currentTarget.style.background = 'transparent'}> ))}
StateDistrictMukimScheme / Area Road NameProperty TypeTenure YearLand (m²)Built-up (m²) Price/m²Transaction Price (RM)
{r.state} {r.district} {r.mukim} {r.area} {r.road} {r.type} {r.tenure} {r.year} {numFmt(r.land)} {numFmt(r.built)} {r.ppsf ? 'RM ' + numFmt(r.ppsf) : '—'} {formatRM(r.price)}
)}
); }; const MiniSelect = ({ value, onChange, options, placeholder }) => ( ); const EmptyState = ({ title, sub }) => (
{title}
{sub}
); Object.assign(window, { TxnTable });