Components (added via the shadcn MCP/CLI): - Navbar: NavigationMenu on desktop, Sheet on mobile; drops Navbar.module.css and the hand-rolled slide-down menu - Buttons: shadcn Button via a shared ContactButtons component (replaces Button.js and its CSS module) - Card for price cards, testimonials, the business block and plan; Table for prices; Input/Textarea/Label/NativeSelect/RadioGroup and Alert in the quote form; Separator in the footer - Components import cn from @/lib/utils (not the new "cn" package); accordion moved to the unified radix-ui package Design tweaks: - Geist Sans is actually applied (font variables were on <body>, but Tailwind reads them on <html>, so the site fell back to system fonts) - Theme primary set to the existing brand blue; slightly smaller radius - Balanced, tighter heading tracking; muted secondary text - 44px contact buttons; 16px FAQ text; tighter homepage spacing - Full-width selects with 16px text on phones (no iOS zoom) Co-Authored-By: Claude Opus 5.5 <[email protected]>
171 lines
5.6 KiB
JavaScript
171 lines
5.6 KiB
JavaScript
import Link from "next/link";
|
||
import { ArrowRight, Check } from "lucide-react";
|
||
import HeaderSm from "./HeaderSm";
|
||
import ContactButtons from "./ContactButtons";
|
||
import CtaBand from "./CtaBand";
|
||
import FaqList from "./FaqList";
|
||
import JsonLd from "./JsonLd";
|
||
import PriceTable from "./PriceTable";
|
||
import { Card, CardContent } from "@/components/ui/card";
|
||
import { business, pageMetadata, serviceSchema } from "@/lib/site";
|
||
import { services } from "@/lib/services";
|
||
|
||
const promises = [
|
||
{
|
||
title: "No fix, no fee",
|
||
text: "If I can't fix it, you don't pay the $35 diagnosis.",
|
||
},
|
||
{
|
||
title: "Price before I start",
|
||
text: "I'll tell you what's wrong and what it costs before any work begins.",
|
||
},
|
||
{
|
||
title: "You deal with me",
|
||
text: "The person you talk to is the person fixing your computer.",
|
||
},
|
||
{
|
||
title: "Local to Te Atatū",
|
||
text: "Based on Te Atatū Peninsula, serving West Auckland and the CBD.",
|
||
},
|
||
];
|
||
|
||
export function serviceMetadata(slug) {
|
||
const { title, description } = services[slug];
|
||
return pageMetadata({ title, description, path: `/services/${slug}` });
|
||
}
|
||
|
||
export default function ServicePage({ slug }) {
|
||
const service = services[slug];
|
||
const path = `/services/${slug}`;
|
||
const testimonial = business.testimonials.find(
|
||
({ name }) => name === service.testimonial
|
||
);
|
||
|
||
return (
|
||
<div>
|
||
<JsonLd
|
||
data={serviceSchema({
|
||
name: service.name,
|
||
description: service.description,
|
||
path,
|
||
})}
|
||
/>
|
||
<HeaderSm text={service.h1} />
|
||
|
||
<section className="container mx-auto max-w-3xl mb-16">
|
||
{service.intro.map((p) => (
|
||
<p key={p} className="text-lg mb-4">
|
||
{p}
|
||
</p>
|
||
))}
|
||
<ContactButtons className="mt-6" />
|
||
</section>
|
||
|
||
<section className="container mx-auto max-w-3xl mb-16">
|
||
<h2 className="text-3xl font-bold mb-4">What I fix</h2>
|
||
<ul className="space-y-2">
|
||
{service.fixes.map((fix) => (
|
||
<li key={fix} className="flex gap-2">
|
||
<Check
|
||
aria-hidden="true"
|
||
className="size-5 shrink-0 mt-0.5 text-primary"
|
||
/>
|
||
{fix}
|
||
</li>
|
||
))}
|
||
</ul>
|
||
{service.fixesNote && <p className="mt-4">{service.fixesNote}</p>}
|
||
</section>
|
||
|
||
<section className="container mx-auto max-w-3xl mb-16">
|
||
<h2 className="text-3xl font-bold mb-4">Pricing</h2>
|
||
<PriceTable items={service.prices} className="mb-4" />
|
||
<p className="mb-2">
|
||
The diagnosis is $35. If I can't fix it, you don't pay it.
|
||
Once I know what's wrong, I'll tell you what the repair will
|
||
cost before I start. If you decide not to go ahead, the $35 still
|
||
applies.
|
||
</p>
|
||
<p className="text-muted-foreground">
|
||
Labour is $35 an hour. Parts are extra.
|
||
</p>
|
||
</section>
|
||
|
||
<section className="container mx-auto max-w-3xl mb-16">
|
||
<h2 className="text-3xl font-bold mb-4">How it works</h2>
|
||
<ul className="list-disc pl-6 space-y-2 mb-4">
|
||
<li>
|
||
<strong>Drop-off:</strong> bring it to {business.address.street},{" "}
|
||
{business.address.suburb}, by appointment.
|
||
</li>
|
||
<li>
|
||
<strong>Pickup or onsite:</strong> I can collect it or come to you
|
||
anywhere in my service area. The travel fee depends on distance.
|
||
</li>
|
||
<li>
|
||
<strong>Remote:</strong> many software problems can be fixed over
|
||
the internet.
|
||
</li>
|
||
</ul>
|
||
<p>
|
||
Most repairs are done within 24–48 hours, although this isn't
|
||
guaranteed. Parts that need ordering usually take about a week. I
|
||
work Monday to Friday, 9am to 6pm, and weekends by appointment.
|
||
</p>
|
||
</section>
|
||
|
||
<section className="bg-slate-100 py-16 mb-16">
|
||
<div className="container mx-auto max-w-3xl">
|
||
<h2 className="text-3xl font-bold mb-6">Why choose me</h2>
|
||
<ul className="grid grid-cols-1 sm:grid-cols-2 gap-6">
|
||
{promises.map(({ title, text }) => (
|
||
<li key={title}>
|
||
<h3 className="font-bold text-lg mb-1">{title}</h3>
|
||
<p>{text}</p>
|
||
</li>
|
||
))}
|
||
</ul>
|
||
{testimonial && (
|
||
<Card className="mt-8 shadow-none">
|
||
<CardContent>
|
||
<figure>
|
||
<blockquote className="mb-3">
|
||
“{testimonial.quote}”
|
||
</blockquote>
|
||
<figcaption className="font-semibold">
|
||
{testimonial.name}
|
||
</figcaption>
|
||
</figure>
|
||
</CardContent>
|
||
</Card>
|
||
)}
|
||
</div>
|
||
</section>
|
||
|
||
<section className="container mx-auto max-w-3xl mb-16">
|
||
<h2 className="text-3xl font-bold mb-4">Questions</h2>
|
||
<FaqList faqs={service.faqs} />
|
||
</section>
|
||
|
||
<CtaBand />
|
||
|
||
<section className="container mx-auto max-w-3xl py-16">
|
||
<h2 className="text-2xl font-bold mb-4">Related services</h2>
|
||
<ul className="space-y-2">
|
||
{service.related.map((related) => (
|
||
<li key={related}>
|
||
<Link
|
||
href={`/services/${related}`}
|
||
className="inline-flex items-center gap-1 font-bold text-primary hover:underline"
|
||
>
|
||
{services[related].name}
|
||
<ArrowRight aria-hidden="true" className="size-4" />
|
||
</Link>
|
||
</li>
|
||
))}
|
||
</ul>
|
||
</section>
|
||
</div>
|
||
);
|
||
}
|