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:
asownerytandClaude Opus 5.5 committed 2026-09-30 20:47:06 +13:00
1 parent 40d3a08050
commit 9f5cf387dc
13 files changed
+858 -53

No files matched your search

+86
View File
@@ -0,0 +1,86 @@
import Link from "next/link";
import HeaderSm from "../components/HeaderSm";
import CtaBand from "../components/CtaBand";
import { business, pageMetadata } from "@/lib/site";
import { areas } from "@/lib/areas";
export const metadata = pageMetadata({
title: "About John | Te Atatu Computers",
description:
"Te Atatu Computers is John, a local computer repairer on Te Atatū Peninsula. One person, straight answers and a price before any work starts.",
path: "/about",
});
const areaSlugs = Object.fromEntries(areas.map(({ name, slug }) => [name, slug]));
export default function AboutPage() {
return (
<div>
<HeaderSm text={`Hi, I'm ${business.ownerName}`} />
<section className="container mx-auto max-w-3xl mb-12">
<p className="text-lg mb-4">
Te Atatu Computers is me, {business.ownerName}. I fix laptops and
PCs, upgrade slow computers, remove viruses and help homes and small
businesses with their IT, from my base on Te Atatū Peninsula.
</p>
<p className="text-lg mb-4">
Because it&apos;s just me, the person you call or text is the person
who fixes your computer. There&apos;s no front desk and no call
centre, and you won&apos;t have to explain the problem twice.
</p>
<p className="text-lg">
I keep things simple: a $35 diagnosis, a clear price before I start,
and if I can&apos;t fix it, you don&apos;t pay the diagnosis fee.
</p>
</section>
<section className="container mx-auto max-w-3xl mb-12">
<h2 className="text-2xl font-bold mb-4">How I work</h2>
<ul className="list-disc pl-6 space-y-2">
<li>
<strong>Drop-off</strong> at {business.address.street},{" "}
{business.address.suburb}, by appointment.
</li>
<li>
<strong>Onsite visits and pickup</strong> across my service area,
with a travel fee based on distance.
</li>
<li>
<strong>Remote support</strong> for software problems.
</li>
<li>
<strong>Hours:</strong> Monday to Friday, 9am to 6pm. Weekends by
appointment.
</li>
<li>
I work on Windows laptops and PCs, Chromebooks and custom-built
computers. I don&apos;t repair Apple devices.
</li>
</ul>
</section>
<section className="container mx-auto max-w-3xl mb-16">
<h2 className="text-2xl font-bold mb-4">Where I work</h2>
<ul className="grid grid-cols-2 gap-2 list-disc pl-6">
{business.serviceArea.map((name) => (
<li key={name}>
{areaSlugs[name] ? (
<Link
href={`/areas/${areaSlugs[name]}`}
className="text-sky-800 hover:underline"
>
{name}
</Link>
) : (
name
)}
</li>
))}
</ul>
</section>
<CtaBand />
</div>
);
}
+131
View File
@@ -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&apos;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&apos;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>
);
}
+159
View File
@@ -0,0 +1,159 @@
import Link from "next/link";
import { Check, 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 {
business,
businessPlan,
links,
pageMetadata,
serviceSchema,
} from "@/lib/site";
const page = {
title: "Small Business IT Support Plan West Auckland",
description:
"IT support for West Auckland small businesses: $65 a month for 2 hours of remote or onsite help. No lock-in. Wi-Fi, email, Microsoft 365, backups.",
path: "/business",
};
export const metadata = pageMetadata(page);
const covered = [
"Wi-Fi, routers and network problems",
"Email setup and problems",
"Microsoft 365 and Google Workspace accounts",
"Printers and scanners",
"New computer setup and moving files across",
"Backups for your business files",
"Security basics: antivirus, updates and passwords",
"Software installation and everyday troubleshooting",
];
const faqs = [
{
question: "What happens if I need more than 2 hours in a month?",
answer:
"Extra time is charged at the normal $35 an hour.",
},
{
question: "Do unused hours carry over?",
answer:
"No. The 2 hours are for that month and don't roll over to the next one.",
},
{
question: "Is there a contract?",
answer: "No lock-in. You can cancel any time.",
},
{
question: "Do I have to be on the plan?",
answer:
"No. You can also book support whenever you need it at $35 an hour, onsite or remote.",
},
];
export default function BusinessPage() {
return (
<div>
<JsonLd
data={serviceSchema({
name: "Small business IT support",
description: page.description,
path: page.path,
})}
/>
<HeaderSm text="Small Business IT Support in West Auckland" />
<section className="container mx-auto max-w-3xl mb-16">
<p className="text-lg mb-4">
Most small businesses don&apos;t need a full-time IT person. They
need someone local to call when the Wi-Fi drops, email stops
working or a new staff member needs a laptop set up.
</p>
<p className="text-lg mb-6">
I support small businesses around Te Atatū and West Auckland, onsite
or remotely. You deal with me directly, every time.
</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">
<div className="rounded-lg border-2 border-sky-800 p-6 md:p-8">
<h2 className="text-2xl font-bold mb-1">Business IT plan</h2>
<p className="mb-4">
<span className="text-4xl font-bold text-sky-800">
{businessPlan.price}
</span>{" "}
a month
</p>
<ul className="space-y-2 mb-4">
{businessPlan.includes.map((item) => (
<li key={item} className="flex gap-2">
<Check
aria-hidden="true"
className="size-5 shrink-0 mt-0.5 text-sky-800"
/>
{item}
</li>
))}
</ul>
<p className="text-slate-600">{businessPlan.terms}</p>
</div>
<p className="mt-4">
Prefer to pay as you go? Support is $35 an hour, onsite or remote.
</p>
</section>
<section className="container mx-auto max-w-3xl mb-16">
<h2 className="text-3xl font-bold mb-4">What&apos;s covered</h2>
<ul className="space-y-2 mb-4">
{covered.map((item) => (
<li key={item} className="flex gap-2">
<Check
aria-hidden="true"
className="size-5 shrink-0 mt-0.5 text-sky-800"
/>
{item}
</li>
))}
</ul>
<p>
Hardware repairs and upgrades are covered too. Parts are extra. See{" "}
<Link
href="/services/it-support"
className="text-sky-800 underline"
>
IT support
</Link>{" "}
and{" "}
<Link
href="/services/computer-repair"
className="text-sky-800 underline"
>
computer repairs
</Link>
.
</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 />
</div>
);
}
+84 -33
View File
@@ -1,43 +1,94 @@
import Link from "next/link";
import { Mail, MapPin, Phone } from "lucide-react";
import { business, links } from "@/lib/site";
import { services } from "@/lib/services";
import { areas } from "@/lib/areas";
const pages = [
{ href: "/pricing", label: "Pricing" },
{ href: "/business", label: "Business IT plans" },
{ href: "/windows-10", label: "Windows 10 help" },
{ href: "/about", label: "About" },
{ href: "/contact", label: "Contact" },
];
export default function Footer() { export default function Footer() {
const { address } = business;
return ( return (
<footer className="bg-slate-800 text-white py-8 text-center"> <footer className="bg-slate-800 text-white py-12">
<div className="flex flex-col items-center"> <div className="container mx-auto grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-4">
<h3 className="flex flex-row items-center justify-center mb-4 space-x-2 hover:underline text-lg font-bold"> <div>
<svg <p className="text-lg font-bold mb-4">{business.name}</p>
xmlns="http://www.w3.org/2000/svg" <ul className="space-y-3">
viewBox="0 0 20 20" <li>
fill="currentColor" <a
width="1em" href={links.tel}
className="flex items-center gap-2 hover:underline"
> >
<path <Phone aria-hidden="true" className="size-4 shrink-0" />
fillRule="evenodd" {business.phone}
d="M2 3.5A1.5 1.5 0 0 1 3.5 2h1.148a1.5 1.5 0 0 1 1.465 1.175l.716 3.223a1.5 1.5 0 0 1-1.052 1.767l-.933.267c-.41.117-.643.555-.48.95a11.542 11.542 0 0 0 6.254 6.254c.395.163.833-.07.95-.48l.267-.933a1.5 1.5 0 0 1 1.767-1.052l3.223.716A1.5 1.5 0 0 1 18 15.352V16.5a1.5 1.5 0 0 1-1.5 1.5H15c-1.149 0-2.263-.15-3.326-.43A13.022 13.022 0 0 1 2.43 8.326 13.019 13.019 0 0 1 2 5V3.5Z"
clipRule="evenodd"
/>
</svg>
<a href="tel:+64211077483">
021 107 7483
</a> </a>
</h3> </li>
<h3 className="flex flex-row items-center justify-center mb-4 space-x-2 hover:underline text-lg font-bold"> <li>
<svg <a
xmlns="http://www.w3.org/2000/svg" href={links.email}
viewBox="0 0 20 20" className="flex items-center gap-2 hover:underline"
fill="currentColor"
width="1em"
> >
<path <Mail aria-hidden="true" className="size-4 shrink-0" />
fillRule="evenodd" {business.email}
d="M5.404 14.596A6.5 6.5 0 1 1 16.5 10a1.25 1.25 0 0 1-2.5 0 4 4 0 1 0-.571 2.06A2.75 2.75 0 0 0 18 10a8 8 0 1 0-2.343 5.657.75.75 0 0 0-1.06-1.06 6.5 6.5 0 0 1-9.193 0ZM10 7.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5Z"
clipRule="evenodd"
/>
</svg>
<a href="mailto:[email protected]">
info@teatatucomputers.com
</a> </a>
</h3> </li>
<li className="flex items-start gap-2">
<MapPin aria-hidden="true" className="size-4 shrink-0 mt-1" />
<span>
{address.street}, {address.suburb}, {address.city}{" "}
{address.postcode}
<br />
{business.hours}
</span>
</li>
</ul>
</div> </div>
<p>Copyright © Te Atatu Computer Repair, {new Date().getFullYear()}</p> <nav aria-label="Services">
<p className="text-lg font-bold mb-4">Services</p>
<ul className="space-y-2">
{Object.entries(services).map(([slug, { name }]) => (
<li key={slug}>
<Link href={`/services/${slug}`} className="hover:underline">
{name}
</Link>
</li>
))}
</ul>
</nav>
<nav aria-label="Areas">
<p className="text-lg font-bold mb-4">Areas</p>
<ul className="space-y-2">
{areas.map(({ slug, name }) => (
<li key={slug}>
<Link href={`/areas/${slug}`} className="hover:underline">
{name}
</Link>
</li>
))}
</ul>
</nav>
<nav aria-label="More">
<p className="text-lg font-bold mb-4">More</p>
<ul className="space-y-2">
{pages.map(({ href, label }) => (
<li key={href}>
<Link href={href} className="hover:underline">
{label}
</Link>
</li>
))}
</ul>
</nav>
</div>
<p className="container mx-auto mt-8 text-sm text-slate-300">
Copyright © {business.name}, {new Date().getFullYear()}
</p>
</footer> </footer>
); );
} }
+36
View File
@@ -141,8 +141,44 @@ export default function Navbar() {
Virus Removal Virus Removal
</Link> </Link>
</li> </li>
<li>
<Link
href="/business"
className={`link ${
pathname === "/business" ? styles.active : ""
}`}
>
Business IT Plans
</Link>
</li>
<li>
<Link
href="/windows-10"
className={`link ${
pathname === "/windows-10" ? styles.active : ""
}`}
>
Windows 10 Help
</Link>
</li>
</ul> </ul>
</li> </li>
<li>
<Link
href="/pricing"
className={`link ${pathname === "/pricing" ? styles.active : ""}`}
>
Pricing
</Link>
</li>
<li>
<Link
href="/about"
className={`link ${pathname === "/about" ? styles.active : ""}`}
>
About
</Link>
</li>
<li> <li>
<Link <Link
href="/#faq" href="/#faq"
+8 -6
View File
@@ -65,7 +65,7 @@
} }
/* Add styles for mobile view */ /* Add styles for mobile view */
@media (max-width: 500px) { @media (max-width: 899px) {
.navbar { .navbar {
background-color: white; background-color: white;
} }
@@ -75,8 +75,10 @@
flex-direction: column; flex-direction: column;
border-radius: 0; border-radius: 0;
/* Hidden by sliding up its own height, so it works for any number of links. */
position: absolute; position: absolute;
top: -14em; top: 4rem;
transform: translateY(-100%);
left: 0; left: 0;
right: 0; right: 0;
z-index: -2; z-index: -2;
@@ -86,7 +88,7 @@
padding-top: 0.5em; padding-top: 0.5em;
padding-bottom: 0.5em; padding-bottom: 0.5em;
transition-property: top border-radius; transition-property: transform, border-radius;
transition-duration: 0.4s; transition-duration: 0.4s;
transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
} }
@@ -104,8 +106,8 @@
border-radius: 0.5em; border-radius: 0.5em;
} }
.active { .links.active {
top: 4rem; transform: translateY(0);
border-radius: 0 0 0.5em 0.5em; border-radius: 0 0 0.5em 0.5em;
} }
@@ -135,7 +137,7 @@
} }
/* Styles for desktop view */ /* Styles for desktop view */
@media (min-width: 501px) { @media (min-width: 900px) {
.links { .links {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
+34 -7
View File
@@ -9,6 +9,7 @@ import FaqList from "./components/FaqList";
import CtaBand from "./components/CtaBand"; import CtaBand from "./components/CtaBand";
import { pageMetadata, business, links } from "@/lib/site"; import { pageMetadata, business, links } from "@/lib/site";
import { homeFaqs } from "@/lib/faqs"; import { homeFaqs } from "@/lib/faqs";
import { areas } from "@/lib/areas";
import { import {
ArrowRight, ArrowRight,
Briefcase, Briefcase,
@@ -198,6 +199,18 @@ export default function Home() {
))} ))}
</ul> </ul>
<SectionCta /> <SectionCta />
<Link
href="/windows-10"
className="mt-8 flex items-center justify-between gap-4 rounded-lg bg-sky-900 p-4 text-white hover:bg-sky-800"
>
<span>
<span className="block font-bold">Still on Windows 10?</span>
<span className="block text-sm">
See your options, with a free Windows 11 compatibility check.
</span>
</span>
<ArrowRight aria-hidden="true" className="size-5 shrink-0" />
</Link>
</section> </section>
<section className="bg-slate-100 py-16 mb-32"> <section className="bg-slate-100 py-16 mb-32">
@@ -274,9 +287,23 @@ export default function Home() {
Onsite visits, pickup and return, and remote support across: Onsite visits, pickup and return, and remote support across:
</p> </p>
<ul className="grid grid-cols-2 gap-2 mb-6 list-disc pl-6"> <ul className="grid grid-cols-2 gap-2 mb-6 list-disc pl-6">
{business.serviceArea.map((area) => ( {business.serviceArea.map((area) => {
<li key={area}>{area}</li> const page = areas.find(({ name }) => name === area);
))} return (
<li key={area}>
{page ? (
<Link
href={`/areas/${page.slug}`}
className="text-sky-800 hover:underline"
>
{area}
</Link>
) : (
area
)}
</li>
);
})}
</ul> </ul>
<p> <p>
Not on the list? Get in touch and I&apos;ll let you know if I can Not on the list? Get in touch and I&apos;ll let you know if I can
@@ -313,15 +340,15 @@ export default function Home() {
<h2 className="text-2xl font-bold mb-2">For local businesses</h2> <h2 className="text-2xl font-bold mb-2">For local businesses</h2>
<p> <p>
IT support for small businesses: Wi-Fi and networks, email, IT support for small businesses: Wi-Fi and networks, email,
backups, security and new PC setup. Onsite or remote, from backups, security and new PC setup. Onsite or remote, $35 an
$35 an hour. hour, or $65 a month for 2 hours of support.
</p> </p>
</div> </div>
<Link <Link
href="/services/it-support" href="/business"
className="inline-flex items-center gap-1 font-bold text-sky-800 hover:underline" className="inline-flex items-center gap-1 font-bold text-sky-800 hover:underline"
> >
Business IT support Business IT plans
<ArrowRight aria-hidden="true" className="size-4" /> <ArrowRight aria-hidden="true" className="size-4" />
</Link> </Link>
</div> </div>
+86
View File
@@ -0,0 +1,86 @@
import Link from "next/link";
import HeaderSm from "../components/HeaderSm";
import CtaBand from "../components/CtaBand";
import { business, pageMetadata, priceList } from "@/lib/site";
export const metadata = pageMetadata({
title: "Computer Repair Prices | Te Atatu Computers",
description:
"Clear prices for laptop & PC repairs in Te Atatū: $35 diagnosis (no fix, no fee), $35/hr labour, SSD upgrades from $45, Windows reinstalls from $45.",
path: "/pricing",
});
export default function PricingPage() {
return (
<div>
<HeaderSm text="Prices" />
<section className="container mx-auto max-w-3xl mb-16">
<p className="text-lg mb-4">
Every job starts with a $35 diagnosis. Then I&apos;ll tell you
what&apos;s wrong and what the repair will cost before I start.
Labour is $35 an hour. The prices below are for labour, and parts
are extra.
</p>
</section>
{priceList.map(({ group, items }) => (
<section key={group} className="container mx-auto max-w-3xl mb-12">
<h2 className="text-2xl font-bold mb-4">{group}</h2>
<table className="w-full text-left">
<tbody>
{items.map(({ item, price }) => (
<tr key={item} className="border-b">
<th scope="row" className="py-3 pr-4 font-normal">
{item}
</th>
<td className="py-3 font-bold text-sky-800">{price}</td>
</tr>
))}
</tbody>
</table>
</section>
))}
<section className="container mx-auto max-w-3xl mb-12">
<h2 className="text-2xl font-bold mb-4">No fix, no fee</h2>
<ul className="list-disc pl-6 space-y-2">
<li>If I can&apos;t fix it, you don&apos;t pay the $35 diagnosis.</li>
<li>
If I can fix it but you decide not to go ahead, the $35 diagnosis
fee applies.
</li>
<li>
I&apos;ll always tell you the cost before starting a repair.
</li>
</ul>
</section>
<section className="container mx-auto max-w-3xl mb-12">
<h2 className="text-2xl font-bold mb-4">Onsite visits & pickup</h2>
<p>
Drop-off at {business.address.street}, {business.address.suburb},
by appointment, has no travel fee. For onsite visits or pickup and
return, the travel fee depends on distance. Get in touch with your
suburb for a quote. Small businesses can also get{" "}
<Link href="/business" className="text-sky-800 underline">
2 hours of support a month for $65
</Link>
.
</p>
</section>
<section className="container mx-auto max-w-3xl mb-16">
<h2 className="text-2xl font-bold mb-4">Payment methods</h2>
<ul className="list-disc pl-6 space-y-1 mb-2">
{business.paymentMethods.map((method) => (
<li key={method}>{method}</li>
))}
</ul>
<p>I don&apos;t accept EFTPOS.</p>
</section>
<CtaBand />
</div>
);
}
+6 -1
View File
@@ -1,7 +1,12 @@
import { SITE_URL, routes } from "@/lib/site"; import { SITE_URL, routes } from "@/lib/site";
import { areas } from "@/lib/areas";
export default function sitemap() { export default function sitemap() {
return routes.map(({ path, priority }) => ({ const areaRoutes = areas.map(({ slug }) => ({
path: `/areas/${slug}`,
priority: 0.6,
}));
return [...routes, ...areaRoutes].map(({ path, priority }) => ({
url: `${SITE_URL}${path}`, url: `${SITE_URL}${path}`,
lastModified: new Date(), lastModified: new Date(),
changeFrequency: "monthly", changeFrequency: "monthly",
+110
View File
@@ -0,0 +1,110 @@
import Link from "next/link";
import { 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 { business, links, pageMetadata } from "@/lib/site";
export const metadata = pageMetadata({
title: "Still on Windows 10? What to Do Now | Te Atatu Computers",
description:
"Windows 10 no longer gets regular updates. Free Windows 11 compatibility check, upgrades, SSD refreshes and file transfers in Te Atatū & West Auckland.",
path: "/windows-10",
});
const options = [
{
title: "Upgrade to Windows 11",
text: "If your computer is eligible, upgrading to Windows 11 keeps it getting security updates. I'll check whether your computer can run it for free, and upgrades start from $35.",
},
{
title: "Upgrade and speed it up",
text: "Many older computers that can run Windows 11 are held back by a slow hard drive. Upgrading to an SSD at the same time (from $45 + the drive) can make it feel new.",
},
{
title: "Move to a new computer",
text: "If your computer can't run Windows 11, or it isn't worth upgrading, I can set up a new one and move your files, photos and email across. File transfers start from $45 and setup from $35.",
},
];
const faqs = [
{
question: "Can my computer run Windows 11?",
answer:
"Some older computers can't officially run Windows 11. I'll check yours for free and tell you your options.",
},
{
question: "Is my Windows 10 computer still safe to use?",
answer:
"It will keep working, but without regular security updates it becomes more exposed to viruses and scams over time. That matters most for online banking, email and shopping.",
},
{
question: "What are Extended Security Updates?",
answer:
"Microsoft sells Extended Security Updates for Windows 10. Businesses can buy them until October 2028. Home users have a different, shorter programme. Check Microsoft's Windows 10 ESU page, or ask me and I'll check what applies to you.",
},
];
export default function Windows10Page() {
return (
<div>
<HeaderSm text="Still on Windows 10? What to do now" />
<section className="container mx-auto max-w-3xl mb-16">
<p className="text-lg mb-4">
Microsoft no longer supports Windows 10 with regular updates. Your
computer will keep working, but without security updates it gets
riskier to use over time.
</p>
<p className="text-lg mb-6">
The good news is that you probably don&apos;t need to rush out and
buy a new computer. Here are your options, and I&apos;m happy to
check which one suits you for free.
</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 for a free check
</Button>
</div>
</section>
<section className="container mx-auto max-w-3xl mb-16">
<h2 className="text-3xl font-bold mb-6">Your options</h2>
<ol className="space-y-6">
{options.map(({ title, text }, i) => (
<li key={title} className="flex gap-4">
<span className="flex size-10 shrink-0 items-center justify-center rounded-full bg-sky-800 font-bold text-white">
{i + 1}
</span>
<div>
<h3 className="text-xl font-bold mb-1">{title}</h3>
<p>{text}</p>
</div>
</li>
))}
</ol>
<p className="mt-6">
Not sure? Get in touch and I&apos;ll give you a straight answer. See
all prices on the{" "}
<Link href="/pricing" className="text-sky-800 underline">
pricing page
</Link>
.
</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 />
</div>
);
}
+61
View File
@@ -0,0 +1,61 @@
// Area pages at /areas/[slug]. Only add suburbs from the owner's service area,
// and give each one genuinely local content (not just a swapped name).
export const areas = [
{
slug: "te-atatu-peninsula",
name: "Te Atatū Peninsula",
intro:
"I'm based on Graham Avenue, right here on Te Atatū Peninsula, so if you live on the Peninsula your local computer repairer is just down the road. Cracked laptop screen, a PC that won't start, a slow computer or a scam pop-up: bring it round, or I'll come to you.",
gettingThere:
"For Peninsula locals, dropping your computer off is usually the quickest and cheapest option. Book a time, bring it to 34 Graham Avenue, and I'll tell you what's wrong and what it costs before I start. If you can't get out, I can do a home visit or collect it. Because you're close by, the travel fee is at the low end.",
faq: {
question: "Can I drop my laptop off today?",
answer:
"Drop-offs are by appointment, so call or text 021 107 7483 and we'll find a time that suits you.",
},
},
{
slug: "te-atatu-south",
name: "Te Atatū South",
intro:
"Te Atatū South is just across the Northwestern Motorway from my base on Te Atatū Peninsula. I fix laptops and PCs, upgrade slow computers, remove viruses and help with everyday IT for homes and small businesses in Te Atatū South.",
gettingThere:
"Most Te Atatū South customers drop their computer off at 34 Graham Avenue on the Peninsula, by appointment. If it's easier, I can come to you or collect it and bring it back. The travel fee depends on distance, so ask for a quote with your street name.",
faq: {
question: "Do you do home visits in Te Atatū South?",
answer:
"Yes. I can come to your home or business in Te Atatū South for onsite support, or collect your computer and return it once it's fixed. There's a travel fee based on distance. Get in touch for a quote.",
},
},
{
slug: "henderson",
name: "Henderson",
intro:
"Need a computer repaired in Henderson? I fix Windows laptops, desktop PCs and Chromebooks for Henderson homes and businesses, from screen and battery replacements to virus removal, SSD upgrades and Wi-Fi problems.",
gettingThere:
"You can drop your computer off at my base on Te Atatū Peninsula by appointment, or I can come to you in Henderson, or collect and return it. Many software problems, like email, slow Windows or pop-ups, can be fixed remotely, so you might not need to go anywhere.",
faq: {
question: "Is it worth coming from Henderson to Te Atatū?",
answer:
"It's a short drive, and drop-off avoids a travel fee. If that doesn't suit, I can come to you or collect the computer for a distance-based fee, or fix software problems remotely.",
},
},
{
slug: "massey",
name: "Massey",
intro:
"I help Massey homes and small businesses with laptop and PC repairs, upgrades, virus removal and IT support. You deal with me directly: one local person who'll tell you what's wrong and what it costs before starting.",
gettingThere:
"Massey customers can drop off at 34 Graham Avenue, Te Atatū Peninsula, by appointment. I can also do onsite visits or pickup and return in Massey for a travel fee based on distance. Remote support is available for software problems.",
faq: {
question: "Can you help my Massey business with IT?",
answer:
"Yes. I support small businesses with networks, email, Microsoft 365 and Google Workspace, backups and new computers. That's $35 an hour, or $65 a month for 2 hours of support.",
},
},
];
export function getArea(slug) {
return areas.find((area) => area.slug === slug);
}
-4
View File
@@ -4,7 +4,6 @@
export const services = { export const services = {
"computer-repair": { "computer-repair": {
name: "Computer & Laptop Repair", name: "Computer & Laptop Repair",
navLabel: "Computer & laptop repairs",
title: "Laptop & PC Repair Te Atatū & West Auckland", title: "Laptop & PC Repair Te Atatū & West Auckland",
description: description:
"Cracked screens, dead laptops, PCs that won't boot and Windows faults. Upfront pricing and honest advice from your local tech in Te Atatū.", "Cracked screens, dead laptops, PCs that won't boot and Windows faults. Upfront pricing and honest advice from your local tech in Te Atatū.",
@@ -61,7 +60,6 @@ export const services = {
"computer-upgrades": { "computer-upgrades": {
name: "Computer Upgrades & Speed-Up", name: "Computer Upgrades & Speed-Up",
navLabel: "Upgrades & speed-up",
title: "SSD Upgrades & PC Speed-Up | Te Atatu Computers", title: "SSD Upgrades & PC Speed-Up | Te Atatu Computers",
description: description:
"Slow computer? An SSD and RAM upgrade can make it feel new for a fraction of the cost of a new PC. Local upgrades in Te Atatū, West Auckland.", "Slow computer? An SSD and RAM upgrade can make it feel new for a fraction of the cost of a new PC. Local upgrades in Te Atatū, West Auckland.",
@@ -110,7 +108,6 @@ export const services = {
"virus-removal": { "virus-removal": {
name: "Virus & Malware Removal", name: "Virus & Malware Removal",
navLabel: "Virus removal",
title: "Virus & Malware Removal West Auckland", title: "Virus & Malware Removal West Auckland",
description: description:
"Pop-ups, scams, slow or hacked PC? I'll clean it up, usually without losing your files, and help stop it happening again. Based in Te Atatū.", "Pop-ups, scams, slow or hacked PC? I'll clean it up, usually without losing your files, and help stop it happening again. Based in Te Atatū.",
@@ -162,7 +159,6 @@ export const services = {
"it-support": { "it-support": {
name: "IT Support", name: "IT Support",
navLabel: "IT support",
title: "Home & Small Business IT Support West Auckland", title: "Home & Small Business IT Support West Auckland",
description: description:
"Onsite and remote IT support for homes and small businesses: Wi-Fi, email, backups, security and new-PC setup.", "Onsite and remote IT support for homes and small businesses: Wi-Fi, email, backups, security and new-PC setup.",
+57 -2
View File
@@ -72,9 +72,59 @@ export const routes = [
{ path: "/services/computer-upgrades", priority: 0.8 }, { path: "/services/computer-upgrades", priority: 0.8 },
{ path: "/services/virus-removal", priority: 0.8 }, { path: "/services/virus-removal", priority: 0.8 },
{ path: "/services/it-support", priority: 0.8 }, { path: "/services/it-support", priority: 0.8 },
{ path: "/pricing", priority: 0.8 },
{ path: "/business", priority: 0.7 },
{ path: "/windows-10", priority: 0.7 },
{ path: "/about", priority: 0.6 },
{ path: "/contact", priority: 0.6 }, { path: "/contact", priority: 0.6 },
]; ];
// Full price list, shown on /pricing. Labour prices; parts are extra.
export const priceList = [
{
group: "Repairs",
items: [
{ item: "Diagnosis", price: "$35" },
{ item: "Laptop screen replacement", price: "from $35 + the screen" },
{ item: "Battery, charging port or keyboard", price: "from $35 + parts" },
{ item: "Other repairs (labour)", price: "$35 an hour" },
],
},
{
group: "Upgrades & Windows",
items: [
{ item: "SSD upgrade", price: "from $45 + the drive" },
{ item: "Windows reinstall", price: "from $45" },
{ item: "Windows 11 compatibility check", price: "Free" },
{ item: "RAM, graphics card and Windows 11 upgrades", price: "from $35 + parts" },
{ item: "Custom PC build (labour)", price: "from $35" },
],
},
{
group: "Viruses, data & support",
items: [
{ item: "Virus and malware removal", price: "from $35" },
{ item: "Moving files to a new computer", price: "from $45" },
{ item: "New computer setup", price: "from $35" },
{ item: "Remote support", price: "$35 an hour" },
{ item: "Onsite support", price: "$35 an hour + travel" },
{ item: "Small business IT plan", price: "$65 a month" },
],
},
];
export const businessPlan = {
price: "$65",
hours: 2,
includes: [
"2 hours of remote or onsite support every month",
"Extra time at the normal $35 an hour",
"No lock-in contract. Cancel any time.",
],
terms:
"Unused hours don't roll over to the next month. Onsite visits have a travel fee based on distance.",
};
// Builds per-page metadata: title, description, canonical, Open Graph and Twitter. // Builds per-page metadata: title, description, canonical, Open Graph and Twitter.
export function pageMetadata({ title, description, path }) { export function pageMetadata({ title, description, path }) {
return { return {
@@ -146,7 +196,12 @@ export function localBusinessSchema() {
}; };
} }
export function serviceSchema({ name, description, path }) { export function serviceSchema({
name,
description,
path,
areas = business.serviceArea,
}) {
return { return {
"@context": "https://schema.org", "@context": "https://schema.org",
"@type": "Service", "@type": "Service",
@@ -155,7 +210,7 @@ export function serviceSchema({ name, description, path }) {
description, description,
url: `${SITE_URL}${path}`, url: `${SITE_URL}${path}`,
provider: { "@id": BUSINESS_ID }, provider: { "@id": BUSINESS_ID },
areaServed: business.serviceArea.map((area) => ({ areaServed: areas.map((area) => ({
"@type": "Place", "@type": "Place",
name: `${area}, Auckland`, name: `${area}, Auckland`,
})), })),