Condo Area

One condo area (Panda /area/<slug>): hero with description, market counters, price stats, neighborhood scores, the area outline map and its buildings with paging.

Root + partsClient component@pandaidx/widgets 1.14.1

Anatomy

Every piece of Condo Area as it nests in code. A root holds the data and state; its parts and hooks only work inside it.

<PandaProvider>Mounted once in app/layout.tsx
<CondoArea>One condo area (Panda /area/<slug>): hero with description, market counters, price stats, neighborhood scores, the area outline map and its…
<CondoAreaMap />

The websites buildings map (price pills in the theme's map pin look, a popup per building with its link, the area…

</CondoArea>
</PandaProvider>

Installation

  1. 1
    npm install <tarball or @pandaidx/widgets>
    Terminal
    npm install https://github.com/pandaidx/panda-widgets/releases/download/v1.14.1/pandaidx-widgets-1.14.1.tgz
  2. 2
    import "@pandaidx/widgets/styles.css" in app/layout.tsx
  3. 3
    Wrap the site in <PandaProvider publicKey apiUrl preset theme> (app/layout.tsx)
  4. 4
    In each page (Server Component) call the widget's server loader, then render the widget with its data
  5. 5
    Allow the images: next.config images.remotePatterns [{ protocol: "https", hostname: "**" }] (listing and blog photos come from several CDNs)

Usage

CondoArea
// app/condos/areas/[slug]/page.tsx
import { notFound } from "next/navigation"
import { CondoArea } from "@pandaidx/widgets/client"
import { fetchCondoArea } from "@pandaidx/widgets/server"
export default async function AreaPage({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params
const data = await fetchCondoArea({ publicKey: process.env.NEXT_PUBLIC_PANDA_PUBLIC_KEY!, apiUrl: process.env.NEXT_PUBLIC_PANDA_API_URL, slug })
if (!data) notFound()
return <CondoArea data={data} map="top" />
}

Examples

An area page in the site's own hero, stats and building cards

CondoArea
// app/area/[slug]/page.tsx
import { notFound } from "next/navigation"
import { fetchCondoArea } from "@pandaidx/widgets/server"
import { AreaView } from "@/components/area-view"
export default async function AreaPage({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params
const data = await fetchCondoArea({ publicKey: process.env.NEXT_PUBLIC_PANDA_PUBLIC_KEY!, apiUrl: process.env.NEXT_PUBLIC_PANDA_API_URL, slug })
if (!data) notFound()
return <AreaView data={data} />
}
// components/area-view.tsx
"use client"
import Link from "next/link"
import { CondoAreaMap, useCondoArea, type SiteCondoAreaResponse } from "@pandaidx/widgets/client"
import { resolveLibraryHref } from "@pandaidx/widgets/search"
const BuildingLink = ({ building }: { building: SiteCondoAreaResponse["buildings"][number] }) => {
const href = resolveLibraryHref(building, "default", { buildingDetail: "/condos/{slug}" })
return href ? <Link href={href}>{building.name}</Link> : <span>{building.name}</span>
}
export const AreaView = ({ data }: { data: SiteCondoAreaResponse }) => {
const area = useCondoArea({ data })
return (
<main className="mx-auto max-w-6xl px-4 py-16">
<h1 className="text-4xl">{area.area.name}</h1>
{area.paragraphs.map((text) => <p key={text} className="mt-4">{text}</p>)}
<dl className="mt-8 grid grid-cols-2 gap-4 md:grid-cols-4">
{area.stats.map((fact) => <div key={fact.label}><dt className="text-sm opacity-70">{fact.label}</dt><dd className="text-xl">{fact.value}</dd></div>)}
</dl>
<CondoAreaMap buildings={area.buildings} boundary={area.area.boundary} routes={{ buildingDetail: "/condos/{slug}" }} className="mt-10 h-96" />
<ul className="mt-10 grid gap-6 md:grid-cols-3">
{area.buildings.map((building) => <li key={building.id}><BuildingLink building={building} /></li>)}
</ul>
<div className="mt-8 flex gap-4">
{area.page > 1 ? <button type="button" onClick={() => area.setPage(area.page - 1)}>Previous</button> : null}
{area.page < area.totalPages ? <button type="button" onClick={() => area.setPage(area.page + 1)}>Next</button> : null}
</div>
</main>
)
}

API reference

CondoArea

One condo area (Panda /area/<slug>): hero with description, market counters, price stats, neighborhood scores, the area outline map and its buildings with paging.

Import from @pandaidx/widgets/client · renders inside PandaProvider

PropTypeDefaultDescription
data*object—Area page (null → notFound()).
sections"overview" | "market" | "stats" | "neighborhood" | "buildings"["overview","market","stats","neighborhood","buildings"]Blocks and their order.
map"side" | "top" | "off""top"Area map beside the buildings, above them, or none.
columnsobject3Building card columns (default 2 with the map beside, else 3).
cardVariant"classic" | "overlay" | "minimal" | "horizontal""classic"Building card design.
buildingHrefstring"/condos/{slug}"Host route of a building.
classNamesobject—Host classes appended to any slot (see slots).
preset"default" | "template1"—Preset for this widget only (defaults to the provider preset).
variant"default" | "template1" | "panda"—panda: the Panda website condo area page (breadcrumb, title, Share, Request Information, section nav, hero, stats, About with clickable condo details, neighborhood features, the area map and its condos with filters / sort / grid / download, request-info band with the agent card) with the website components (pass fetchPandaCondoArea() data; every other design prop is ignored). default / template1: deprecated alias of preset.
themeobject—Token overrides for this widget only, merged over the provider theme (same shape as PandaProvider.theme).

Parts

<CondoAreaMap />

The websites buildings map (price pills in the theme's map pin look, a popup per building with its link, the area outline, zoom controls at the kit's small control size) for useCondoArea().buildings, useCondoDirectory().buildings or useCondoDirectory({ pins: true }).pins; boundary takes area.boundary; routes ({ buildingDetail: "/condos/{slug}" }) points the popup links at your building pages. It fills its parent: give it a height with className. hoveredId / onHover link it to your building cards.

Hooks

useCondoArea()inside PandaProvider

useCondoArea({ data }) → { area: { slug, name, image, description, boundary… }, paragraphs (the description), market: { key, label, today, value, change, changeText }[] (For Sale, For Rent, Pending, Sold…; zeros left out), stats: { label, value }[] (formatted averages, price ranges and counts; empty ones left out), scores: { label, score, rating }[] (Walk, Bike, Transit), highlights: { label, text }[] (restaurants, parks…), buildings (current page; link each with its path), total, page, pageSize, totalPages, setPage(page), loading } — data comes from fetchCondoArea (null → notFound()). setPage loads the buildings of another page in place (the previous page stays in buildings while it loads). Draw the hero, counters, stats, scores and building cards in the site's style; CondoArea stays the ready-made page. Client component only.

Server data

Loaders run in a Server Component and come from @pandaidx/widgets/server. Typed data is for a page you design yourself, rendered inside the root.

fetchCondoArea()widget loader

GET /v1/condos/areas/:slug (page from the URL); null for an unknown area. Returns SiteCondoAreaResponse | null → data

fetchPandaCondoArea()loader of variant="panda"

Data of variant="panda" (GET /v1/condos/directory/area/:slug): the area, the directory areas, the last market totals, the public application and the request-information form; null for an unknown area (notFound()). Returns PandaCondoAreaData | null → data