AllAi/apps/web/app/[locale]/pricing/page.tsx
2025-11-14 21:54:04 +03:00

94 lines
2.6 KiB
TypeScript

import type { Metadata } from "next";
import { notFound } from "next/navigation";
import { loadDictionary } from "@allai/i18n/server";
import { locales, resolveLocale } from "@/config/i18n";
import { PricingPage, pricingPlans } from "@/features/marketing/PricingPage";
import { absoluteUrl, buildCanonical, buildLocaleAlternates, buildOpenGraph, buildTwitterCard } from "@/seo/seoUtils";
const PATH = "/pricing" as const;
type PageProps = {
params: { locale: string };
};
export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
const locale = resolveLocale(params.locale);
if (!locales.includes(locale)) {
return {
title: "Pricing"
};
}
const dictionary = await loadDictionary(locale);
const pricingDictionary = dictionary.marketing.pricing;
const title = `Pricing | ${dictionary.common.brandLong ?? dictionary.common.brandShort}`;
const description = pricingDictionary.heroSubtitle;
return {
title,
description,
alternates: {
canonical: buildCanonical(locale, PATH),
languages: buildLocaleAlternates(PATH)
},
openGraph: buildOpenGraph({
locale,
title,
description,
path: PATH
}),
twitter: buildTwitterCard({
title,
description
})
};
}
export default async function PricingRoute({ params }: PageProps) {
const locale = resolveLocale(params.locale);
if (!locales.includes(locale)) {
notFound();
}
const dictionary = await loadDictionary(locale);
const pricingDictionary = dictionary.marketing.pricing;
const canonical = buildCanonical(locale, PATH);
const offers = pricingPlans.map((plan) => {
const copy = pricingDictionary.plans[plan.id];
return {
"@type": "Offer",
name: copy.title,
description: copy.description,
price: plan.price,
priceCurrency: "RUB",
availability: "https://schema.org/InStock",
url: `${canonical}#${plan.id}`
};
});
const structuredData = {
"@context": "https://schema.org",
"@type": "Product",
name: `${dictionary.common.brandLong ?? dictionary.common.brandShort} plans`,
description:
"Coin packs for AI image and video generation with collaboration tooling included. Coins never expire and map 1:1 to rubles.",
url: canonical,
image: absoluteUrl("/favicon.ico"),
offers
};
return (
<>
<script
type="application/ld+json"
suppressHydrationWarning
dangerouslySetInnerHTML={{ __html: JSON.stringify(structuredData) }}
/>
<PricingPage locale={locale} dictionary={dictionary} />
</>
);
}