diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 77d7c4c8..c589bcbb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -85,7 +85,7 @@ jobs: timeout-minutes: 60 services: mariadb: - image: public.ecr.aws/docker/library/mariadb:10.6 + image: mariadb:10.6 env: MARIADB_ROOT_PASSWORD: ci-root-only MARIADB_DATABASE: creator_platform @@ -164,7 +164,7 @@ jobs: timeout-minutes: 45 services: mariadb: - image: public.ecr.aws/docker/library/mariadb:10.6 + image: mariadb:10.6 env: MARIADB_ROOT_PASSWORD: ci-root-only MARIADB_DATABASE: creator_platform @@ -277,7 +277,7 @@ jobs: timeout-minutes: 20 services: mariadb: - image: public.ecr.aws/docker/library/mariadb:10.6 + image: mariadb:10.6 env: MARIADB_ROOT_PASSWORD: ci-root-only MARIADB_DATABASE: creator_platform diff --git a/apps/web/public/solution-art/businesses.svg b/apps/web/public/solution-art/businesses.svg new file mode 100644 index 00000000..47ae6109 --- /dev/null +++ b/apps/web/public/solution-art/businesses.svg @@ -0,0 +1 @@ +Brand content illustrationOriginal editorial composition showing a campaign planning board, creative cards and connected output formats.BRAND / 04MESSAGECREATIVE OUTPUTVISUALIDEA → CREATE → REFINE \ No newline at end of file diff --git a/apps/web/public/solution-art/ecommerce.svg b/apps/web/public/solution-art/ecommerce.svg new file mode 100644 index 00000000..46e4052a --- /dev/null +++ b/apps/web/public/solution-art/ecommerce.svg @@ -0,0 +1 @@ +Retail creative illustrationOriginal editorial composition showing a campaign planning board, creative cards and connected output formats.PRODUCT / 02PRODUCTCREATIVE OUTPUT01CAMPAIGNIDEA → CREATE → REFINE \ No newline at end of file diff --git a/apps/web/public/solution-art/explorer.svg b/apps/web/public/solution-art/explorer.svg new file mode 100644 index 00000000..ea8f02c8 --- /dev/null +++ b/apps/web/public/solution-art/explorer.svg @@ -0,0 +1 @@ +Creative solutions illustrationOriginal editorial composition showing a campaign planning board, creative cards and connected output formats.ONE WORKSPACECONCEPTCREATIVE OUTPUTEVERY FORMATIDEA → CREATE → REFINE \ No newline at end of file diff --git a/apps/web/public/solution-art/marketing-agencies.svg b/apps/web/public/solution-art/marketing-agencies.svg new file mode 100644 index 00000000..4e5f1de8 --- /dev/null +++ b/apps/web/public/solution-art/marketing-agencies.svg @@ -0,0 +1 @@ +Agency campaign illustrationOriginal editorial composition showing a campaign planning board, creative cards and connected output formats.CAMPAIGN / 01BRIEFCREATIVE OUTPUTMOTIONIDEA → CREATE → REFINE \ No newline at end of file diff --git a/apps/web/public/solution-art/production-teams.svg b/apps/web/public/solution-art/production-teams.svg new file mode 100644 index 00000000..75d87da1 --- /dev/null +++ b/apps/web/public/solution-art/production-teams.svg @@ -0,0 +1 @@ +Production flow illustrationOriginal editorial composition showing a campaign planning board, creative cards and connected output formats.STUDIO / 05PLANCREATIVE OUTPUTDELIVERIDEA → CREATE → REFINE \ No newline at end of file diff --git a/apps/web/public/solution-art/social-media-creators.svg b/apps/web/public/solution-art/social-media-creators.svg new file mode 100644 index 00000000..5e1780eb --- /dev/null +++ b/apps/web/public/solution-art/social-media-creators.svg @@ -0,0 +1 @@ +Social stories illustrationOriginal editorial composition showing a campaign planning board, creative cards and connected output formats.SOCIAL / 03STORYCREATIVE OUTPUTFORMATIDEA → CREATE → REFINE \ No newline at end of file diff --git a/apps/web/src/app/page.tsx b/apps/web/src/app/page.tsx index 8e8361f2..0de33ed9 100644 --- a/apps/web/src/app/page.tsx +++ b/apps/web/src/app/page.tsx @@ -186,6 +186,7 @@ export default function HomePage() { > {[ ["/tools", "Explore Tools"], + ["/solutions", "Solutions"], ["#platform", "Platform"], ["#models", "Models"], ["#workflow", "Workflow"], diff --git a/apps/web/src/app/pricing/page.tsx b/apps/web/src/app/pricing/page.tsx index 59e9aabe..b36ac617 100644 --- a/apps/web/src/app/pricing/page.tsx +++ b/apps/web/src/app/pricing/page.tsx @@ -81,6 +81,9 @@ export default async function PricingPage() { FAQ + + Solutions + Learn diff --git a/apps/web/src/app/sitemap.ts b/apps/web/src/app/sitemap.ts index ec9d3d82..29641d6f 100644 --- a/apps/web/src/app/sitemap.ts +++ b/apps/web/src/app/sitemap.ts @@ -3,6 +3,7 @@ import { db } from "@aiwa/db"; import { publishedContent } from "@aiwa/learn"; import { learnOrigin } from "@/lib/learn/queries"; import { publicTools } from "@/lib/public-tools"; +import { solutions } from "@/lib/public-solutions"; import { getPublicPricingCatalogue } from "@/lib/public-pricing"; export const dynamic = "force-dynamic"; export default async function sitemap(): Promise { @@ -22,6 +23,10 @@ export default async function sitemap(): Promise { { url: origin }, { url: `${origin}/pricing` }, { url: `${origin}/tools` }, + { url: `${origin}/solutions` }, + ...solutions.map((solution) => ({ + url: origin + "/solutions/" + solution.slug, + })), ...publicTools.map((tool) => ({ url: `${origin}/tools/${tool.slug}` })), ...operations.map((item) => ({ url: `${origin}/tools/operations/${encodeURIComponent(item.id)}`, diff --git a/apps/web/src/app/solutions/[slug]/page.tsx b/apps/web/src/app/solutions/[slug]/page.tsx new file mode 100644 index 00000000..9d02fbf5 --- /dev/null +++ b/apps/web/src/app/solutions/[slug]/page.tsx @@ -0,0 +1,343 @@ +import type { Metadata, Route } from "next"; +import Image from "next/image"; +import Link from "next/link"; +import { notFound } from "next/navigation"; +import { Icon } from "@/components/ui/icon"; +import { + SectionTitle, + SolutionCard, + SolutionFaq, + SolutionsConversion, + SolutionsFooter, + SolutionsHeader, +} from "@/components/marketing/solutions/solution-components"; +import { SolutionModels } from "@/components/marketing/solutions/solution-models"; +import { getPublicPricingCatalogue } from "@/lib/public-pricing"; +import { publicToolById } from "@/lib/public-tools"; +import { + solutionBySlug, + solutions, + solutionHref, +} from "@/lib/public-solutions"; + +export const dynamic = "force-dynamic"; +type PageProps = { params: Promise<{ slug: string }> }; +export function generateStaticParams() { + return solutions.map((solution) => ({ slug: solution.slug })); +} +export async function generateMetadata({ + params, +}: PageProps): Promise { + const solution = solutionBySlug((await params).slug); + if (!solution) + return { title: "Solution not found", robots: { index: false } }; + return { + title: solution.name + " — Creative AI Workflows & Outcomes", + description: solution.description.slice(0, 160), + alternates: { canonical: solutionHref(solution) }, + robots: { index: true, follow: true }, + openGraph: { + title: solution.headline + " · Aiwa Creators", + description: solution.description, + type: "website", + images: [ + { + url: solution.cover, + width: 920, + height: 650, + alt: solution.name + " illustration", + }, + ], + }, + }; +} + +export default async function SolutionDetailPage({ params }: PageProps) { + const solution = solutionBySlug((await params).slug); + if (!solution) notFound(); + const catalogue = await getPublicPricingCatalogue().catch(() => ({ + models: [], + })); + const origin = process.env.APP_URL ?? "https://creator.aiwamediagroup.com"; + const breadcrumbs = [ + { name: "Home", item: "/" }, + { name: "Solutions", item: "/solutions" }, + { name: solution.name, item: solutionHref(solution) }, + ]; + const schema = { + "@context": "https://schema.org", + "@type": "BreadcrumbList", + itemListElement: breadcrumbs.map((item, i) => ({ + "@type": "ListItem", + position: i + 1, + name: item.name, + item: new URL(item.item, origin).toString(), + })), + }; + const related = solution.related + .map(solutionBySlug) + .filter((item) => item !== undefined); + return ( +
+ +