Build the full site from the copy decks
Adds About, Contact, Services (index + 8) and Industries (index + 7), generated from structured JSON converted out of the .docx copy decks by scripts/convert-docs.py. Shared header with dropdowns, footer, contact form (mailto until a backend exists), FAQ/breadcrumb JSON-LD, sitemap and robots. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
import type { Metadata } from "next";
|
||||
import type { Page } from "./pages";
|
||||
|
||||
export function pageMetadata(page: Page): Metadata {
|
||||
const title = page.meta.title ?? "AI Growth Technologies";
|
||||
const description = page.meta.description ?? undefined;
|
||||
return {
|
||||
title,
|
||||
description,
|
||||
alternates: { canonical: page.path },
|
||||
openGraph: { type: "website", url: page.path, siteName: "AI Growth Technologies", title, description },
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
import about from "@/content/pages/about.json";
|
||||
import contact from "@/content/pages/contact.json";
|
||||
import services from "@/content/pages/services.json";
|
||||
import industries from "@/content/pages/industries.json";
|
||||
import paidMedia from "@/content/pages/services_paid-media.json";
|
||||
import seo from "@/content/pages/services_seo.json";
|
||||
import llmo from "@/content/pages/services_llmo.json";
|
||||
import webDevelopment from "@/content/pages/services_web-development.json";
|
||||
import socialMedia from "@/content/pages/services_social-media.json";
|
||||
import brand from "@/content/pages/services_brand.json";
|
||||
import consulting from "@/content/pages/services_consulting.json";
|
||||
import cybersecurity from "@/content/pages/services_cybersecurity.json";
|
||||
import hospitality from "@/content/pages/industries_hospitality.json";
|
||||
import medicalDental from "@/content/pages/industries_medical-dental.json";
|
||||
import automotive from "@/content/pages/industries_automotive.json";
|
||||
import ecommerce from "@/content/pages/industries_e-commerce.json";
|
||||
import homeServices from "@/content/pages/industries_home-services.json";
|
||||
import b2b from "@/content/pages/industries_b2b.json";
|
||||
import creators from "@/content/pages/industries_creators.json";
|
||||
|
||||
import { industryNav, serviceNav } from "./routes";
|
||||
|
||||
export type SectionKind = "text" | "cards" | "steps" | "faq" | "links" | "stats" | "offices" | "showcase";
|
||||
|
||||
export type Item = {
|
||||
kicker: string | null;
|
||||
title: string;
|
||||
subtitle: string | null;
|
||||
body: string[];
|
||||
list: string[];
|
||||
cta: string | null;
|
||||
num?: string;
|
||||
};
|
||||
|
||||
export type Section = {
|
||||
eyebrow: string | null;
|
||||
title: string | null;
|
||||
intro: string[];
|
||||
items: Item[];
|
||||
outro: string[];
|
||||
ctas: string[];
|
||||
form: boolean;
|
||||
kind: SectionKind | string;
|
||||
};
|
||||
|
||||
export type Page = {
|
||||
slug: string;
|
||||
path: string;
|
||||
meta: { title: string | null; description: string | null };
|
||||
hero: Section;
|
||||
sections: Section[];
|
||||
};
|
||||
|
||||
const serviceContent: Record<string, unknown> = {
|
||||
"paid-media": paidMedia,
|
||||
seo,
|
||||
llmo,
|
||||
"web-development": webDevelopment,
|
||||
"social-media": socialMedia,
|
||||
brand,
|
||||
consulting,
|
||||
cybersecurity,
|
||||
};
|
||||
|
||||
const industryContent: Record<string, unknown> = {
|
||||
hospitality,
|
||||
"medical-dental": medicalDental,
|
||||
automotive,
|
||||
"e-commerce": ecommerce,
|
||||
"home-services": homeServices,
|
||||
b2b,
|
||||
creators,
|
||||
};
|
||||
|
||||
export const pages = { about, contact, services, industries } as unknown as Record<
|
||||
"about" | "contact" | "services" | "industries",
|
||||
Page
|
||||
>;
|
||||
|
||||
export function getService(slug: string): Page | undefined {
|
||||
return serviceNav.some((s) => s.slug === slug) ? (serviceContent[slug] as Page) : undefined;
|
||||
}
|
||||
|
||||
export function getIndustry(slug: string): Page | undefined {
|
||||
return industryNav.some((s) => s.slug === slug) ? (industryContent[slug] as Page) : undefined;
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
// Route map and navigation. Kept free of page content so client components can import it.
|
||||
|
||||
export const SITE_URL = "https://www.aigrowthtechnologies.com";
|
||||
export const CONTACT_EMAIL = "info@aigrowthtechnologies.com";
|
||||
|
||||
export const serviceNav = [
|
||||
{ slug: "paid-media", name: "Paid Media" },
|
||||
{ slug: "seo", name: "SEO" },
|
||||
{ slug: "llmo", name: "LLMO & AI Visibility" },
|
||||
{ slug: "web-development", name: "Web Development" },
|
||||
{ slug: "social-media", name: "Social Media" },
|
||||
{ slug: "brand", name: "Brand" },
|
||||
{ slug: "consulting", name: "Consulting" },
|
||||
{ slug: "cybersecurity", name: "Cybersecurity" },
|
||||
] as const;
|
||||
|
||||
export const industryNav = [
|
||||
{ slug: "hospitality", name: "Hospitality" },
|
||||
{ slug: "medical-dental", name: "Medical & Dental" },
|
||||
{ slug: "automotive", name: "Automotive" },
|
||||
{ slug: "e-commerce", name: "E-Commerce" },
|
||||
{ slug: "home-services", name: "Home Services" },
|
||||
{ slug: "b2b", name: "B2B & SaaS" },
|
||||
{ slug: "creators", name: "Creators & Personal Brands" },
|
||||
] as const;
|
||||
|
||||
export const mainNav = [
|
||||
{ label: "Services", href: "/services", children: serviceNav.map((s) => ({ label: s.name, href: `/services/${s.slug}` })) },
|
||||
{ label: "Industries", href: "/industries", children: industryNav.map((s) => ({ label: s.name, href: `/industries/${s.slug}` })) },
|
||||
{ label: "About", href: "/about" },
|
||||
{ label: "Contact", href: "/contact" },
|
||||
];
|
||||
|
||||
// Industry keywords are checked before services so "Creators & Personal Brands" never resolves to Brand.
|
||||
const industryKeys: [RegExp, string][] = [
|
||||
[/HOSPITALITY/, "hospitality"],
|
||||
[/MEDICAL|DENTAL/, "medical-dental"],
|
||||
[/AUTOMOTIVE/, "automotive"],
|
||||
[/E-COMMERCE/, "e-commerce"],
|
||||
[/HOME SERVICES/, "home-services"],
|
||||
[/B2B/, "b2b"],
|
||||
[/CREATOR/, "creators"],
|
||||
];
|
||||
|
||||
const serviceKeys: [RegExp, string][] = [
|
||||
[/PAID MEDIA|ATRILYX/, "paid-media"],
|
||||
[/LLMO|AI VISIBILITY/, "llmo"],
|
||||
[/\bSEO\b|MESEO/, "seo"],
|
||||
[/WEB|WEBSITE/, "web-development"],
|
||||
[/SOCIAL/, "social-media"],
|
||||
[/BRAND/, "brand"],
|
||||
[/CONSULTING/, "consulting"],
|
||||
[/CYBERSECURITY|KORRITH/, "cybersecurity"],
|
||||
];
|
||||
|
||||
/** Resolve a CTA label from the copy deck to a route. Anything that asks for a conversation goes to contact. */
|
||||
export function hrefFor(label: string, currentPath = ""): string {
|
||||
const t = label.toUpperCase();
|
||||
const contact = currentPath === "/contact" ? "#contact-form" : "/contact";
|
||||
if (/\bALL SERVICES\b|OUR SERVICES/.test(t)) return "/services";
|
||||
if (/ALL INDUSTRIES|OUR INDUSTRIES|^EXPLORE INDUSTRIES/.test(t)) return "/industries";
|
||||
if (/DISCOVER AI GROWTH|MEET OUR TEAM|OUR APPROACH/.test(t)) return "/about";
|
||||
if (/^(EXPLORE|SEE|EXAMINE|VIEW|LEARN)\b/.test(t)) {
|
||||
for (const [re, slug] of industryKeys) if (re.test(t)) return `/industries/${slug}`;
|
||||
for (const [re, slug] of serviceKeys) if (re.test(t)) return `/services/${slug}`;
|
||||
}
|
||||
return contact;
|
||||
}
|
||||
Reference in New Issue
Block a user