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
+858
-53
No files matched your search
@@ -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's just me, the person you call or text is the person
|
||||
who fixes your computer. There's no front desk and no call
|
||||
centre, and you won'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't fix it, you don'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'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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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'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'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>
|
||||
);
|
||||
}
|
||||
@@ -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() {
|
||||
const { address } = business;
|
||||
return (
|
||||
<footer className="bg-slate-800 text-white py-8 text-center">
|
||||
<div className="flex flex-col items-center">
|
||||
<h3 className="flex flex-row items-center justify-center mb-4 space-x-2 hover:underline text-lg font-bold">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
width="1em"
|
||||
<footer className="bg-slate-800 text-white py-12">
|
||||
<div className="container mx-auto grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-4">
|
||||
<div>
|
||||
<p className="text-lg font-bold mb-4">{business.name}</p>
|
||||
<ul className="space-y-3">
|
||||
<li>
|
||||
<a
|
||||
href={links.tel}
|
||||
className="flex items-center gap-2 hover:underline"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
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
|
||||
<Phone aria-hidden="true" className="size-4 shrink-0" />
|
||||
{business.phone}
|
||||
</a>
|
||||
</h3>
|
||||
<h3 className="flex flex-row items-center justify-center mb-4 space-x-2 hover:underline text-lg font-bold">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
width="1em"
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href={links.email}
|
||||
className="flex items-center gap-2 hover:underline"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
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
|
||||
<Mail aria-hidden="true" className="size-4 shrink-0" />
|
||||
{business.email}
|
||||
</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>
|
||||
<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>
|
||||
);
|
||||
}
|
||||
@@ -141,8 +141,44 @@ export default function Navbar() {
|
||||
Virus Removal
|
||||
</Link>
|
||||
</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>
|
||||
</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>
|
||||
<Link
|
||||
href="/#faq"
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
}
|
||||
|
||||
/* Add styles for mobile view */
|
||||
@media (max-width: 500px) {
|
||||
@media (max-width: 899px) {
|
||||
.navbar {
|
||||
background-color: white;
|
||||
}
|
||||
@@ -75,8 +75,10 @@
|
||||
flex-direction: column;
|
||||
border-radius: 0;
|
||||
|
||||
/* Hidden by sliding up its own height, so it works for any number of links. */
|
||||
position: absolute;
|
||||
top: -14em;
|
||||
top: 4rem;
|
||||
transform: translateY(-100%);
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: -2;
|
||||
@@ -86,7 +88,7 @@
|
||||
padding-top: 0.5em;
|
||||
padding-bottom: 0.5em;
|
||||
|
||||
transition-property: top border-radius;
|
||||
transition-property: transform, border-radius;
|
||||
transition-duration: 0.4s;
|
||||
transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
|
||||
}
|
||||
@@ -104,8 +106,8 @@
|
||||
border-radius: 0.5em;
|
||||
}
|
||||
|
||||
.active {
|
||||
top: 4rem;
|
||||
.links.active {
|
||||
transform: translateY(0);
|
||||
border-radius: 0 0 0.5em 0.5em;
|
||||
}
|
||||
|
||||
@@ -135,7 +137,7 @@
|
||||
}
|
||||
|
||||
/* Styles for desktop view */
|
||||
@media (min-width: 501px) {
|
||||
@media (min-width: 900px) {
|
||||
.links {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
+34
-7
@@ -9,6 +9,7 @@ import FaqList from "./components/FaqList";
|
||||
import CtaBand from "./components/CtaBand";
|
||||
import { pageMetadata, business, links } from "@/lib/site";
|
||||
import { homeFaqs } from "@/lib/faqs";
|
||||
import { areas } from "@/lib/areas";
|
||||
import {
|
||||
ArrowRight,
|
||||
Briefcase,
|
||||
@@ -198,6 +199,18 @@ export default function Home() {
|
||||
))}
|
||||
</ul>
|
||||
<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 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:
|
||||
</p>
|
||||
<ul className="grid grid-cols-2 gap-2 mb-6 list-disc pl-6">
|
||||
{business.serviceArea.map((area) => (
|
||||
<li key={area}>{area}</li>
|
||||
))}
|
||||
{business.serviceArea.map((area) => {
|
||||
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>
|
||||
<p>
|
||||
Not on the list? Get in touch and I'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>
|
||||
<p>
|
||||
IT support for small businesses: Wi-Fi and networks, email,
|
||||
backups, security and new PC setup. Onsite or remote, from
|
||||
$35 an hour.
|
||||
backups, security and new PC setup. Onsite or remote, $35 an
|
||||
hour, or $65 a month for 2 hours of support.
|
||||
</p>
|
||||
</div>
|
||||
<Link
|
||||
href="/services/it-support"
|
||||
href="/business"
|
||||
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" />
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
@@ -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'll tell you
|
||||
what'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't fix it, you don'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'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't accept EFTPOS.</p>
|
||||
</section>
|
||||
|
||||
<CtaBand />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
+6
-1
@@ -1,7 +1,12 @@
|
||||
import { SITE_URL, routes } from "@/lib/site";
|
||||
import { areas } from "@/lib/areas";
|
||||
|
||||
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}`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: "monthly",
|
||||
|
||||
@@ -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't need to rush out and
|
||||
buy a new computer. Here are your options, and I'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'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>
|
||||
);
|
||||
}
|
||||
@@ -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,7 +4,6 @@
|
||||
export const services = {
|
||||
"computer-repair": {
|
||||
name: "Computer & Laptop Repair",
|
||||
navLabel: "Computer & laptop repairs",
|
||||
title: "Laptop & PC Repair Te Atatū & West Auckland",
|
||||
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ū.",
|
||||
@@ -61,7 +60,6 @@ export const services = {
|
||||
|
||||
"computer-upgrades": {
|
||||
name: "Computer Upgrades & Speed-Up",
|
||||
navLabel: "Upgrades & speed-up",
|
||||
title: "SSD Upgrades & PC Speed-Up | Te Atatu Computers",
|
||||
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.",
|
||||
@@ -110,7 +108,6 @@ export const services = {
|
||||
|
||||
"virus-removal": {
|
||||
name: "Virus & Malware Removal",
|
||||
navLabel: "Virus removal",
|
||||
title: "Virus & Malware Removal West Auckland",
|
||||
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ū.",
|
||||
@@ -162,7 +159,6 @@ export const services = {
|
||||
|
||||
"it-support": {
|
||||
name: "IT Support",
|
||||
navLabel: "IT support",
|
||||
title: "Home & Small Business IT Support West Auckland",
|
||||
description:
|
||||
"Onsite and remote IT support for homes and small businesses: Wi-Fi, email, backups, security and new-PC setup.",
|
||||
|
||||
+57
-2
@@ -72,9 +72,59 @@ export const routes = [
|
||||
{ path: "/services/computer-upgrades", priority: 0.8 },
|
||||
{ path: "/services/virus-removal", 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 },
|
||||
];
|
||||
|
||||
// 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.
|
||||
export function pageMetadata({ title, description, path }) {
|
||||
return {
|
||||
@@ -146,7 +196,12 @@ export function localBusinessSchema() {
|
||||
};
|
||||
}
|
||||
|
||||
export function serviceSchema({ name, description, path }) {
|
||||
export function serviceSchema({
|
||||
name,
|
||||
description,
|
||||
path,
|
||||
areas = business.serviceArea,
|
||||
}) {
|
||||
return {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "Service",
|
||||
@@ -155,7 +210,7 @@ export function serviceSchema({ name, description, path }) {
|
||||
description,
|
||||
url: `${SITE_URL}${path}`,
|
||||
provider: { "@id": BUSINESS_ID },
|
||||
areaServed: business.serviceArea.map((area) => ({
|
||||
areaServed: areas.map((area) => ({
|
||||
"@type": "Place",
|
||||
name: `${area}, Auckland`,
|
||||
})),
|
||||
|
||||
Reference in new issue
Block a user