Phase 4: pricing, about, area, Windows 10 and business pages
- /pricing: full price list, no-fix-no-fee terms, travel and payments - /about: first-person page from confirmed facts, links to area pages - /areas/[suburb]: Te Atatū Peninsula, Te Atatū South, Henderson and Massey, generated from src/lib/areas.js with suburb-specific copy - /windows-10: options page with free Windows 11 check; October 2028 used for business Extended Security Updates - /business: $65/month plan (2 hours, no lock-in, no rollover) - Nav gains Pricing/About and Business/Windows 10 links; footer rebuilt with address, hours and links; homepage links areas, /business and a Windows 10 banner; sitemap includes all new routes - Mobile menu hides by its own height (fixes overflow with more links); hamburger now used below 900px so the link row never overlaps Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
40d3a08050
commit
9f5cf387dc
13 files changed
+862
-57
No files matched your search
@@ -0,0 +1,131 @@
|
||||
import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import { ArrowRight, MessageSquare, Phone } from "lucide-react";
|
||||
import HeaderSm from "../../components/HeaderSm";
|
||||
import Button from "../../components/Button";
|
||||
import CtaBand from "../../components/CtaBand";
|
||||
import FaqList from "../../components/FaqList";
|
||||
import JsonLd from "../../components/JsonLd";
|
||||
import { areas, getArea } from "@/lib/areas";
|
||||
import { services } from "@/lib/services";
|
||||
import { business, links, pageMetadata, serviceSchema } from "@/lib/site";
|
||||
|
||||
export const dynamicParams = false;
|
||||
|
||||
export function generateStaticParams() {
|
||||
return areas.map(({ slug }) => ({ suburb: slug }));
|
||||
}
|
||||
|
||||
export async function generateMetadata({ params }) {
|
||||
const area = getArea((await params).suburb);
|
||||
return pageMetadata({
|
||||
title: `Computer Repair ${area.name} | Te Atatu Computers`,
|
||||
description: `Laptop & PC repairs, upgrades, virus removal and IT support in ${area.name}. $35 diagnosis, no fix no fee. Call or text ${business.phone}.`,
|
||||
path: `/areas/${area.slug}`,
|
||||
});
|
||||
}
|
||||
|
||||
export default async function AreaPage({ params }) {
|
||||
const area = getArea((await params).suburb);
|
||||
if (!area) notFound();
|
||||
|
||||
const faqs = [
|
||||
area.faq,
|
||||
{
|
||||
question: "How much will it cost?",
|
||||
answer:
|
||||
"A diagnosis is $35, and if I can't fix it you don't pay it. Labour is $35 an hour and parts are extra. I'll tell you the cost before I start.",
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<JsonLd
|
||||
data={serviceSchema({
|
||||
name: `Computer repair in ${area.name}`,
|
||||
description: area.intro,
|
||||
path: `/areas/${area.slug}`,
|
||||
areas: [area.name],
|
||||
})}
|
||||
/>
|
||||
<HeaderSm text={`Computer & Laptop Repair in ${area.name}`} />
|
||||
|
||||
<section className="container mx-auto max-w-3xl mb-16">
|
||||
<p className="text-lg mb-6">{area.intro}</p>
|
||||
<div className="flex flex-col sm:flex-row gap-3">
|
||||
<Button href={links.tel} variant="primary">
|
||||
<Phone aria-hidden="true" />
|
||||
Call {business.phone}
|
||||
</Button>
|
||||
<Button href={links.sms} variant="outlineDark">
|
||||
<MessageSquare aria-hidden="true" />
|
||||
Text me
|
||||
</Button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="container mx-auto max-w-3xl mb-16">
|
||||
<h2 className="text-3xl font-bold mb-4">
|
||||
Getting your computer to me
|
||||
</h2>
|
||||
<p className="mb-4">{area.gettingThere}</p>
|
||||
<p>
|
||||
I work Monday to Friday, 9am to 6pm, and weekends by appointment.
|
||||
Most repairs are done within 24–48 hours, although this isn't
|
||||
guaranteed.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="container mx-auto max-w-3xl mb-16">
|
||||
<h2 className="text-3xl font-bold mb-4">
|
||||
What I can help with in {area.name}
|
||||
</h2>
|
||||
<ul className="space-y-2 mb-4">
|
||||
{Object.entries(services).map(([slug, service]) => (
|
||||
<li key={slug}>
|
||||
<Link
|
||||
href={`/services/${slug}`}
|
||||
className="inline-flex items-center gap-1 font-bold text-sky-800 hover:underline"
|
||||
>
|
||||
{service.name}
|
||||
<ArrowRight aria-hidden="true" className="size-4" />
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<p>
|
||||
See all prices on the{" "}
|
||||
<Link href="/pricing" className="text-sky-800 underline">
|
||||
pricing page
|
||||
</Link>
|
||||
. I don't repair Apple devices.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="container mx-auto max-w-3xl mb-16">
|
||||
<h2 className="text-3xl font-bold mb-4">Questions</h2>
|
||||
<FaqList faqs={faqs} />
|
||||
</section>
|
||||
|
||||
<CtaBand />
|
||||
|
||||
<section className="container mx-auto max-w-3xl py-16">
|
||||
<h2 className="text-2xl font-bold mb-4">Other areas I serve</h2>
|
||||
<ul className="flex flex-wrap gap-x-6 gap-y-2">
|
||||
{areas
|
||||
.filter(({ slug }) => slug !== area.slug)
|
||||
.map(({ slug, name }) => (
|
||||
<li key={slug}>
|
||||
<Link
|
||||
href={`/areas/${slug}`}
|
||||
className="text-sky-800 hover:underline"
|
||||
>
|
||||
{name}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user