/* ======================================================================
KOREALM — Public: hero, listings, filters, property detail
====================================================================== */
function PropertyCard({ p, onOpen, fav, onFav }) {
const { user } = useApp();
const ll = user(p.landlordId);
return (
onOpen(p.id)}>
{ e.stopPropagation(); onFav(p.id); }}>
{p.furnished &&
Furnished }
{window.fmt.money(p.price)}pcm
{p.type}
{p.title}
{p.neighborhood}
{p.beds === 0 ? "Studio" : `${p.beds} bd`}
{p.baths} ba
{p.sqft.toLocaleString()} sq ft
EPC {p.epc}
Council Tax {p.councilTax}
);
}
function FilterBar({ f, setF, count }) {
const types = ["Flat", "Loft", "Studio", "Townhouse"];
return (
setF({ ...f, furnished: !f.furnished })}> Furnished
setF({ ...f, avail: !f.avail })}> Available only
setF({ ...f, pets: !f.pets })}> Pet friendly
{count} home{count !== 1 ? "s" : ""}
setF({ ...f, sort: e.target.value })} style={{ width: 160 }}>
Newest first
Price: low to high
Price: high to low
);
}
function pageWindow(cur, total) {
const out = [];
const set = [...new Set([1, total, cur, cur - 1, cur + 1].filter(n => n >= 1 && n <= total))].sort((a, b) => a - b);
let prev = 0;
set.forEach(n => { if (n - prev > 1) out.push("\u2026"); out.push(n); prev = n; });
return out;
}
function Listings({ onOpen }) {
const { db } = useApp();
const [f, setF] = useState({ q: "", hood: "", type: "", beds: "", max: "", furnished: false, avail: false, pets: false, sort: "new" });
const [favs, setFavs] = useState({});
const [page, setPage] = useState(1);
const toggleFav = (id) => setFavs(s => ({ ...s, [id]: !s[id] }));
const PAGE_SIZE = 12, MAX_PAGES = 20;
const list = useMemo(() => {
let r = db.properties.filter(p => {
if (f.q && !(p.title + " " + p.address).toLowerCase().includes(f.q.toLowerCase())) return false;
if (f.hood && p.neighborhood !== f.hood) return false;
if (f.type && p.type !== f.type) return false;
if (f.beds !== "" && p.beds < +f.beds) return false;
if (f.max && p.price > +f.max) return false;
if (f.furnished && !p.furnished) return false;
if (f.avail && p.status !== "available") return false;
if (f.pets && !p.amenities.some(a => /pet/i.test(a))) return false;
return true;
});
if (f.sort === "low") r = [...r].sort((a, b) => a.price - b.price);
else if (f.sort === "high") r = [...r].sort((a, b) => b.price - a.price);
else r = [...r].sort((a, b) => new Date(b.listedDate) - new Date(a.listedDate));
return r;
}, [db.properties, f]);
useEffect(() => { setPage(1); }, [f]);
const totalPages = Math.min(MAX_PAGES, Math.max(1, Math.ceil(list.length / PAGE_SIZE)));
const cur = Math.min(page, totalPages);
const start = (cur - 1) * PAGE_SIZE;
const pageItems = list.slice(start, start + PAGE_SIZE);
const goPage = (n) => {
setPage(n);
const el = document.getElementById("listings-anchor");
if (el) window.scrollTo({ top: el.getBoundingClientRect().top + window.scrollY - 70, behavior: "smooth" });
};
return (
{list.length === 0 ? (
Try widening your price range or clearing a filter.
) : (
<>
{totalPages > 1 && (
Showing {start + 1}–{Math.min(start + PAGE_SIZE, list.length)} of {list.length} · page {cur} of {totalPages}
goPage(cur - 1)}> Prev
{pageWindow(cur, totalPages).map((n, i) => n === "\u2026"
? …
: goPage(n)}>{n} )}
goPage(cur + 1)}>Next
)}
>
)}
);
}
function Hero({ onBrowse }) {
return (
Now letting across Manchester
Renting, without the runaround.
Browse verified homes, book a viewing, apply, and pay your move-in — all in one trusted place. Korealm handles the rest with the landlord.
Browse homes
How it works
240+ verified listings
48h avg. to approval
100% KYC-verified landlords
);
}
/* ---------------- Detail ---------------- */
function PropertyDetail({ id, onBack }) {
const { db, user, session, go, setSession } = useApp();
const p = db.properties.find(x => x.id === id);
const [gate, setGate] = useState(null); // 'inspection' | 'apply'
if (!p) return
;
const ll = user(p.landlordId);
const kyc = db.kyc.find(k => k.landlordId === p.landlordId);
const go2 = (action) => {
// Already signed in as a tenant → straight into the flow, no prompt.
if (session.tenantId) {
const view = action === "apply" ? "apply" : "book-inspection";
setSession(s => ({ ...s, role: "tenant", view, params: { propertyId: p.id } }));
return;
}
setGate(action);
};
return (
Back to listings
{p.type}
{p.furnished && Furnished }
EPC {p.epc}
Council Tax {p.councilTax}
Listed {window.fmt.fmtShort(p.listedDate)}
{p.title}
{p.address} · {p.neighborhood}, Manchester {p.postcode}
{[["bed", p.beds === 0 ? "Studio" : p.beds + " Bedrooms"], ["bath", p.baths + " Bathrooms"], ["ruler", p.sqft.toLocaleString() + " sq ft"], ["key", window.fmt.money(p.deposit) + " deposit"]].map(([ic, t]) => (
{t}
))}
About this home
A bright {p.type.toLowerCase()} in the heart of {p.neighborhood}, finished to a high standard with abundant natural light and thoughtful storage throughout. Moments from the tram, independent cafés, and green space — an easy place to call home.
Amenities
{p.amenities.map(a =>
{a}
)}
{/* sticky action rail */}
{window.fmt.money(p.price)} pcm
Tenancy deposit (5 weeks) {window.fmt.money(p.deposit)}
Holding deposit (1 week) {window.fmt.money(p.holdingDeposit)}
Move-in total {window.fmt.money(p.price + p.deposit + p.holdingDeposit)}
go2("apply")}>{p.status === "leased" ? "No longer available" : "Apply to rent"}
go2("inspection")}>Book a viewing
Free to apply · No obligation to rent
Listed by
{ll.company}
{kyc?.status === "verified" && }
{ll.name} · Landlord
{kyc?.status === "verified" && Identity & ownership verified by Korealm
}
{gate &&
setGate(null)} />}
);
}
/* Auth gate: sign in or create an account, then continue to the flow */
function AuthGate({ action, property, onClose }) {
const { setSession } = useApp();
const isApply = action === "apply";
const view = isApply ? "apply" : "book-inspection";
const startAuth = (mode) => setSession(s => ({ ...s, role: "public", view: "auth", params: { mode, intent: { role: "tenant", view, params: { propertyId: property.id } } } }));
return (
Cancel}>
Sign in to your Korealm tenant account, or create one — we'll take you straight to {isApply ? "your application" : "booking your viewing"}.
startAuth("signin")}>Sign in
startAuth("register")}>Create an account
);
}
/* ---------------- Public footer ---------------- */
function PubFooter() {
return (
The trusted way to rent. Verified homes, verified landlords, protected payments.
{[["Renters", ["Browse homes", "How it works", "Tenant guide", "Help center"]], ["Landlords", ["List a property", "Pricing", "KYC & payouts", "Owner login"]], ["Company", ["About", "Careers", "Trust & safety", "Contact"]]].map(([h, items]) => (
{h}
{items.map(i =>
{i} )}
))}
© 2026 Korealm Ltd · POC sandbox
);
}
/* ---------------- Public router ---------------- */
function PublicApp() {
const { session, go } = useApp();
useEffect(() => {
const h = () => go("auth", { mode: "register" });
document.addEventListener("korealm-register", h);
return () => document.removeEventListener("korealm-register", h);
}, [go]);
if (session.view === "auth") return ;
if (session.view === "detail") return (
);
// home
return (
{ const el = document.getElementById("listings-anchor"); if (el) window.scrollTo({ top: el.getBoundingClientRect().top + window.scrollY - 70, behavior: "smooth" }); }} />
Homes in Manchester
Every listing is verified before it goes live.
go("detail", { id })} />
);
}
Object.assign(window, { PublicApp, PropertyCard, PropertyDetail });