import { useMemo } from 'react' import PublicLayout from '../../components/PublicLayout.jsx' import PageHeader from '../../components/PageHeader.jsx' import { Loading, ErrorState } from '../../components/PageState.jsx' import { useAsync } from '../../lib/useAsync.js' import { useShardFeed } from '../../lib/useShardFeed.js' import { api } from '../../api/client.js' // PUBLIC houses board: only houses in danger (IDOC), shown by location. Owner, // price, decay detail and the full registry are staff-only (admin Houses view). // Loaded from /public/shard/houses (IDOC-only), kept live by house.decay: a // house entering IDOC appears, one leaving it drops off. const HOUSE_KINDS = new Set(['house.decay']) function HouseRow({ h }) { return (
) } export default function Houses() { const { loading, error, data } = useAsync(() => api.shard.houses()) const { events, connected } = useShardFeed({ filter: HOUSE_KINDS, max: 60 }) // Merge the IDOC snapshot with live house.decay deltas by serial: entering IDOC // adds/updates the row; anything else (refreshed, collapsed) drops it. const board = useMemo(() => { const map = new Map() for (const h of data || []) if (h && h.serial) map.set(h.serial, h) for (let i = events.length - 1; i >= 0; i -= 1) { const ev = events[i] if (ev.kind !== 'house.decay' || !ev.serial) continue if (String(ev.to).toUpperCase() === 'IDOC') { map.set(ev.serial, { serial: ev.serial, name: ev.name, region: ev.region, map: ev.map, x: ev.x, y: ev.y, z: ev.z, isIdoc: true }) } else { map.delete(ev.serial) } } return [...map.values()].sort((a, b) => (a.region || '').localeCompare(b.region || '')) }, [data, events]) return (
{connected ? 'Live' : 'Offline'}
{loading && } {error && } {!loading && !error && ( board.length === 0 ? (

No houses are collapsing right now.

) : ( <>

{board.length} in danger

{board.map((h) => )}
) )}
) }