/* 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 => (
setYearMode(m)} style={{
border: 0, background: yearMode === m ? C.deep : 'transparent',
color: yearMode === m ? C.cream : C.mid, borderRadius: 5,
padding: '2px 8px', fontSize: 10, fontWeight: 600, cursor: 'pointer',
fontFamily: "'DM Sans',sans-serif", textTransform: 'capitalize',
}}>{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 && (
{ setTypes(availTypes); setYr({ single: '', from: '', to: '' }); setPrice({ min: '', max: '' }); }}
style={{
border: `1px dashed ${C.muted}`, background: 'transparent', color: C.mid,
borderRadius: 9999, padding: '8px 14px', fontFamily: "'DM Sans',sans-serif",
fontSize: 13, fontWeight: 500, cursor: 'pointer',
}}>Clear filters
)}
)}
{/* body */}
{loading ? (
Loading property transactions…
) : (txns && txns.length === 0) ? (
) : (filtered.length === 0) ? (
) : (
State District Mukim Scheme / Area
Road Name Property Type Tenure
Year Land (m²) Built-up (m²)
Price/m² Transaction Price (RM)
{filtered.map((r, i) => (
e.currentTarget.style.background = C.cream}
onMouseLeave={e => e.currentTarget.style.background = 'transparent'}>
{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 }) => (
onChange(e.target.value)} style={{
background: C.cream, border: `1px solid ${C.earth}40`, color: C.deep,
fontFamily: "'DM Sans',sans-serif", fontSize: 13, borderRadius: 8,
padding: '8px 10px', appearance: 'none', cursor: 'pointer', flex: 1, minWidth: 0,
backgroundImage: `url("data:image/svg+xml;utf8, ")`,
backgroundRepeat: 'no-repeat', backgroundPosition: 'right 9px center',
}}>
{placeholder}
{options.map(o => {o} )}
);
const EmptyState = ({ title, sub }) => (
);
Object.assign(window, { TxnTable });