Files
computer-repair/src/app/components/ServicePage.js
T
asownerytandClaude Opus 5.5 f916f058fc Swap to shadcn components and polish the design
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]>
2026-09-30 21:25:07 +13:00

171 lines
5.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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&apos;t fix it, you don&apos;t pay it.
Once I know what&apos;s wrong, I&apos;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&apos;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">
&ldquo;{testimonial.quote}&rdquo;
</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>
);
}