"use client";
import * as React from "react";
import { MotionConfig, useReducedMotion } from "motion/react";
import { cn } from "@/lib/utils";
import { defaultAgencyData, type AgencyData, type Listing } from "./data";
import { Mortgage, Neighbourhoods, SoldStats } from "./sections/guides";
import { DEFAULT_FILTERS, Hero, type Filters } from "./sections/hero";
import { Listings } from "./sections/listings";
import { Navbar } from "./sections/navbar";
import { Agents, Footer, Testimonials, Valuation, type ValuationRequest } from "./sections/people";
import { HP_SERIF_STACK, scrollToId, useMoney } from "./sections/ui";
export type { AgencyData, Listing, Filters, ValuationRequest };
export { defaultAgencyData };
export interface RealEstateAgencyTemplateProps {
/** Override any top-level content group (brand, listings, agents…). Missing groups fall back to the demo content. */
data?: Partial<AgencyData>;
/** Called when a visitor runs a property search. */
onSearch?: (filters: Filters) => void;
/** Called when a listing is saved (`true`) or unsaved (`false`). */
onSaveListing?: (listing: Listing, saved: boolean) => void;
/** Called when the valuation form is submitted. Return a promise to keep the loading state. */
onValuationRequest?: (request: ValuationRequest) => void | Promise<void>;
/** Called when someone subscribes to listing alerts. */
onSubscribe?: (email: string) => void;
className?: string;
}
/**
* Harbor & Pine — a warm, editorial real-estate agency landing page.
* Animated harbour hero with property search, listings with photo carousels, neighbourhood guides,
* sold-stats counters, a mortgage calculator with amortization chart, agents, an instant-valuation form,
* auto-advancing testimonials and a footer with listing alerts.
*/
export function RealEstateAgencyTemplate({ data, onSearch, onSaveListing, onValuationRequest, onSubscribe, className }: RealEstateAgencyTemplateProps) {
const d: AgencyData = React.useMemo(() => ({ ...defaultAgencyData, ...data }), [data]);
const reduce = useReducedMotion();
const money = useMoney(d.brand.currency);
const [filters, setFilters] = React.useState<Filters>(DEFAULT_FILTERS);
const [saved, setSaved] = React.useState<Set<string>>(() => new Set(["l2"]));
const [valuation, setValuation] = React.useState({ address: "", nonce: 0 });
const search = () => {
onSearch?.(filters);
scrollToId("listings", reduce);
};
const toggleSave = (id: string) => {
const on = !saved.has(id);
setSaved((cur) => {
const n = new Set(cur);
if (on) n.add(id);
else n.delete(id);
return n;
});
const l = d.listings.find((x) => x.id === id);
if (l) onSaveListing?.(l, on);
};
const toValuation = (address = "") => {
setValuation((v) => ({ address, nonce: v.nonce + 1 }));
scrollToId("sell", reduce);
};
return (
<MotionConfig reducedMotion="user">
<div className={cn("relative w-full bg-background text-foreground antialiased selection:bg-[#24543f]/20", className)} style={{ "--hp-serif": HP_SERIF_STACK } as React.CSSProperties}>
<Navbar brand={d.brand} links={d.nav} onValuation={() => toValuation()} />
<main>
<Hero data={d} filters={filters} setFilters={setFilters} onSearch={search} onValuation={toValuation} />
<Listings data={d} money={money} filters={filters} setFilters={setFilters} saved={saved} onSave={toggleSave} />
<Neighbourhoods
data={d}
money={money}
onBrowse={(area) => {
setFilters({ ...DEFAULT_FILTERS, mode: filters.mode, area });
scrollToId("listings", reduce);
}}
/>
<SoldStats stats={d.stats} />
<Mortgage config={d.mortgage} money={money} />
<Agents agents={d.agents} />
<Valuation data={d} money={money} prefill={valuation} onSubmit={onValuationRequest} />
<Testimonials items={d.testimonials} agents={d.agents} />
</main>
<Footer data={d} onSubscribe={onSubscribe} />
</div>
</MotionConfig>
);
}