Grid of property cards with optional filter tabs and a view-all footer (from Marbrook)
"use client";
import { ArrowUpRight } from "lucide-react";
import Link from "next/link";
import { useState } from "react";
import { Button } from "@/components/ui/button";
import {
Empty,
EmptyContent,
EmptyDescription,
EmptyTitle,
} from "@/components/ui/empty";
import { Tabs, TabsList, TabsTab } from "@/components/ui/tabs";
import { cn } from "@/lib/utils";
import type { Surface } from "../_types/common";
import type { PropertyGridContent } from "../_types/property";
import { defaultProperty1Content } from "./data";
import { PropertyCard } from "./property-card";
import { SectionHeader, surfaceClass, toneFor } from "./section-header";
/** A three-across grid of property cards, with optional filter tabs and a
* "view all" footer. Without tabs it doubles as "similar homes". */
export default function Property1({
content = defaultProperty1Content,
surface = "muted",
}: {
content?: PropertyGridContent;
surface?: Surface;
}) {
const [filter, setFilter] = useState(content.filters?.[0]?.value ?? "all");
const visible =
filter === "all"
? content.properties
: content.properties.filter((property) => property.category === filter);
const tabs = content.filters && content.filters.length > 0 && (
<Tabs onValueChange={(value) => setFilter(String(value))} value={filter}>
<TabsList
className="w-full gap-0 border border-border bg-background p-0 sm:w-fit"
variant="default"
>
{content.filters.map((item) => (
<TabsTab
className="h-11 px-4 text-muted-foreground text-sm data-active:text-primary sm:h-11 sm:text-sm"
key={item.value}
value={item.value}
>
{item.label}
</TabsTab>
))}
</TabsList>
</Tabs>
);
return (
<section className={cn("py-20 sm:py-28", surfaceClass[surface])}>
<div className="mx-auto max-w-7xl px-5 sm:px-8 lg:px-12">
<SectionHeader
action={
tabs ||
(content.action && !content.summary && (
<Link
className="group inline-flex items-center gap-2 text-primary text-sm underline-offset-4 hover:underline"
href={content.action.href}
>
{content.action.label}
<ArrowUpRight className="size-4 transition-transform duration-300 group-hover:translate-x-0.5 group-hover:-translate-y-0.5" />
</Link>
))
}
actionAt={tabs ? "lg" : "sm"}
className="border-border border-b pb-8"
description={content.description}
eyebrow={content.eyebrow}
title={content.title}
tone={toneFor(surface)}
/>
{visible.length > 0 ? (
<div className="mt-10 grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
{visible.map((property, index) => (
<PropertyCard
key={property.href}
priority={index < 3}
property={property}
/>
))}
</div>
) : (
<Empty className="mt-10 rounded-lg border border-border border-dashed bg-card py-16">
<EmptyTitle className="font-display font-normal text-2xl text-primary">
{content.empty.title}
</EmptyTitle>
<EmptyDescription>{content.empty.description}</EmptyDescription>
{content.empty.actions && content.empty.actions.length > 0 && (
<EmptyContent className="mt-6 flex flex-row flex-wrap justify-center gap-3">
{content.empty.actions.map((action) => (
<Button
className="border-none bg-primary px-7 text-primary-foreground shadow-none hover:bg-primary/90"
key={action.href}
render={<Link href={action.href} />}
size="xl"
>
{action.label}
</Button>
))}
</EmptyContent>
)}
</Empty>
)}
{content.summary && content.action && (
<div className="mt-12 flex flex-wrap items-center justify-between gap-6 border-border border-t pt-8">
<p className="text-muted-foreground text-sm">
{content.summary
.replace("{visible}", String(visible.length))
.replace("{total}", String(content.properties.length))}
</p>
<Button
className="group gap-2 border-none bg-primary px-7 text-primary-foreground shadow-none hover:bg-primary/90"
render={<Link href={content.action.href} />}
size="xl"
>
{content.action.label}
<ArrowUpRight className="size-4 transition-transform duration-300 group-hover:translate-x-0.5 group-hover:-translate-y-0.5" />
</Button>
</div>
)}
</div>
</section>
);
}