← back to Homesonspec
apps/web/src/app/communities/[slug]/page.tsx
105 lines
import Link from "next/link";
import { notFound } from "next/navigation";
import { prisma } from "@homesonspec/database";
import { fmtPrice, VerificationBadge } from "@homesonspec/shared-ui";
export const dynamic = "force-dynamic";
export default async function CommunityPage({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params;
const community = await prisma.community.findUnique({
where: { slug },
include: {
builder: true,
floorPlans: true,
salesOffices: true,
incentives: true,
homes: { where: { status: "PUBLISHED", isDemo: false }, orderBy: { publishedAt: "desc" } },
},
});
if (!community || community.isDemo) notFound();
return (
<div className="mx-auto max-w-6xl px-4 py-8">
<h1 className="text-3xl font-bold">{community.name}</h1>
<p className="mt-1 text-neutral-600">
{community.city}, {community.state} {community.zip} · {community.metro} ·{" "}
<Link href={`/builders/${community.builder.slug}`} className="text-teal-700 hover:underline">
{community.builder.name}
</Link>
</p>
<div className="mt-2 flex flex-wrap gap-4 text-sm text-neutral-600">
{community.priceMin && community.priceMax && (
<span>From {fmtPrice(Number(community.priceMin))} to {fmtPrice(Number(community.priceMax))}</span>
)}
{community.schoolDistrict && <span>School district: {community.schoolDistrict}</span>}
<span>
HOA:{" "}
{community.hoaFeeMonthly ? `$${Number(community.hoaFeeMonthly)}/mo` : community.hoaRequired === false ? "None" : "Not published"}
</span>
{community.ageRestricted && <span className="font-medium text-violet-700">Age-restricted community</span>}
</div>
{community.incentives.length > 0 && (
<section className="mt-6">
<h2 className="text-xl font-semibold">Current offers</h2>
<ul className="mt-2 grid gap-3 sm:grid-cols-2">
{community.incentives.map((incentive) => (
<li key={incentive.id} className="rounded-lg border border-amber-200 bg-amber-50 p-3 text-sm">
<div className="font-semibold">{incentive.title}</div>
<div className="mt-1 text-xs text-neutral-500">
{incentive.expiresAt ? `Expires ${incentive.expiresAt.toLocaleDateString()}` : incentive.evergreenLabel}
</div>
</li>
))}
</ul>
</section>
)}
<section className="mt-8">
<h2 className="text-xl font-semibold">Available homes ({community.homes.length})</h2>
<div className="mt-3 grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
{community.homes.map((home) => (
<Link key={home.id} href={`/homes/${home.id}`}
className="rounded-xl border border-neutral-200 p-4 shadow-sm hover:border-teal-400">
<div className="font-semibold">{fmtPrice(home.price === null ? null : Number(home.price))}</div>
<div className="mt-0.5 text-sm text-neutral-600">{home.street}</div>
<div className="text-sm text-neutral-500">
{home.beds ?? "—"} bd · {home.bathsTotal?.toString() ?? "—"} ba · {home.sqft?.toLocaleString() ?? "—"} sqft
</div>
<div className="mt-2"><VerificationBadge label={home.verificationLabel} /></div>
</Link>
))}
</div>
</section>
<section className="mt-8">
<h2 className="text-xl font-semibold">Floor plans</h2>
<div className="mt-3 grid gap-4 sm:grid-cols-3">
{community.floorPlans.map((plan) => (
<Link key={plan.id} href={`/plans/${plan.id}`}
className="rounded-lg border border-neutral-200 p-3 text-sm hover:border-teal-400">
<div className="font-semibold">{plan.name}</div>
<div className="text-neutral-500">
{plan.beds}–{plan.bedsMax ?? plan.beds} bd · {plan.sqft?.toLocaleString()} sqft · from{" "}
{fmtPrice(plan.basePrice === null ? null : Number(plan.basePrice))}
</div>
</Link>
))}
</div>
</section>
{community.salesOffices.length > 0 && (
<section className="mt-8 text-sm text-neutral-600">
<h2 className="text-xl font-semibold text-neutral-900">Sales office</h2>
{community.salesOffices.map((office) => (
<p key={office.id} className="mt-2">
{office.street}, {office.city}, {office.state} {office.zip} · {office.phone}
</p>
))}
</section>
)}
</div>
);
}