feat: add blocks section to marketing navigation and routes
- Introduce new blocks routes for hero, pricing, features, FAQ, stats, call-to-action, and content components - Update marketing navigation configuration to include new blocks menu section - Add corresponding route entries in `routes.ts` - Create placeholder pages for block components - Update localization files to support new blocks menu items - Modify pricing page to include pricing comparator component - Refactor pricing component styling for consistency
This commit is contained in:
parent
63b549212e
commit
416e184a59
@ -2,7 +2,7 @@
|
||||
"Common": {
|
||||
"login": "Login",
|
||||
"logout": "Log out",
|
||||
"signUp": "Sign up"
|
||||
"signUp": "Sign up"
|
||||
},
|
||||
"HomePage": {
|
||||
"title": "next-intl example"
|
||||
@ -77,7 +77,7 @@
|
||||
"allPosts": "All Posts"
|
||||
},
|
||||
"Marketing": {
|
||||
"menu": {
|
||||
"navbar": {
|
||||
"features": {
|
||||
"title": "Features"
|
||||
},
|
||||
@ -144,6 +144,32 @@
|
||||
"description": "The legal agreement between you and our company"
|
||||
}
|
||||
}
|
||||
},
|
||||
"blocks": {
|
||||
"title": "Blocks",
|
||||
"items": {
|
||||
"hero": {
|
||||
"title": "Hero Blocks"
|
||||
},
|
||||
"pricing": {
|
||||
"title": "Pricing Blocks"
|
||||
},
|
||||
"features": {
|
||||
"title": "Features Blocks"
|
||||
},
|
||||
"faq": {
|
||||
"title": "Faq Blocks"
|
||||
},
|
||||
"stats": {
|
||||
"title": "Stats Blocks"
|
||||
},
|
||||
"callToAction": {
|
||||
"title": "Call to Action Blocks"
|
||||
},
|
||||
"content": {
|
||||
"title": "Content Blocks"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"footer": {
|
||||
@ -185,26 +211,26 @@
|
||||
"settings": "Settings"
|
||||
}
|
||||
},
|
||||
"mail": {
|
||||
"common": {
|
||||
"Mail": {
|
||||
"common": {
|
||||
"team": "{name} Team",
|
||||
"copyright": "Copyright {year} All Rights Reserved."
|
||||
},
|
||||
"verifyEmail": {
|
||||
"title": "Hi, {name}.",
|
||||
"body": "Please click the link below to verify your email address.",
|
||||
"confirmEmail": "Confirm email",
|
||||
"subject": "Verify your email"
|
||||
},
|
||||
"forgotPassword": {
|
||||
"title": "Hi, {name}.",
|
||||
"body": "Please click the link below to reset your password.",
|
||||
"resetPassword": "Reset password",
|
||||
"subject": "Reset your password"
|
||||
},
|
||||
"subscribeNewsletter": {
|
||||
"body": "Thank you for subscribing to the newsletter. We will keep you updated with the latest news and updates.",
|
||||
"subject": "Thanks for subscribing"
|
||||
}
|
||||
}
|
||||
},
|
||||
"verifyEmail": {
|
||||
"title": "Hi, {name}.",
|
||||
"body": "Please click the link below to verify your email address.",
|
||||
"confirmEmail": "Confirm email",
|
||||
"subject": "Verify your email"
|
||||
},
|
||||
"forgotPassword": {
|
||||
"title": "Hi, {name}.",
|
||||
"body": "Please click the link below to reset your password.",
|
||||
"resetPassword": "Reset password",
|
||||
"subject": "Reset your password"
|
||||
},
|
||||
"subscribeNewsletter": {
|
||||
"body": "Thank you for subscribing to the newsletter. We will keep you updated with the latest news and updates.",
|
||||
"subject": "Thanks for subscribing"
|
||||
}
|
||||
}
|
||||
}
|
@ -74,7 +74,7 @@
|
||||
"allPosts": "全部文章"
|
||||
},
|
||||
"Marketing": {
|
||||
"menu": {
|
||||
"navbar": {
|
||||
"features": {
|
||||
"title": "功能"
|
||||
},
|
||||
@ -141,6 +141,32 @@
|
||||
"description": "关于您与我们公司之间的法律协议和条款"
|
||||
}
|
||||
}
|
||||
},
|
||||
"blocks": {
|
||||
"title": "内置组件",
|
||||
"items": {
|
||||
"hero": {
|
||||
"title": "Hero组件"
|
||||
},
|
||||
"pricing": {
|
||||
"title": "Pricing组件"
|
||||
},
|
||||
"features": {
|
||||
"title": "Features组件"
|
||||
},
|
||||
"faq": {
|
||||
"title": "Faq组件"
|
||||
},
|
||||
"stats": {
|
||||
"title": "Stats组件"
|
||||
},
|
||||
"callToAction": {
|
||||
"title": "Call to Action组件"
|
||||
},
|
||||
"content": {
|
||||
"title": "Content组件"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"footer": {
|
||||
@ -183,7 +209,7 @@
|
||||
"logout": "退出"
|
||||
}
|
||||
},
|
||||
"mail": {
|
||||
"Mail": {
|
||||
"common": {
|
||||
"team": "{name} 团队",
|
||||
"copyright": "版权所有 {year} All Rights Reserved."
|
||||
|
29
src/app/[locale]/(marketing)/blocks/hero/page.tsx
Normal file
29
src/app/[locale]/(marketing)/blocks/hero/page.tsx
Normal file
@ -0,0 +1,29 @@
|
||||
import Pricing3 from "@/components/nsui/pricing3";
|
||||
import Pricing4 from "@/components/nsui/pricing4";
|
||||
import Pricing5 from "@/components/nsui/pricing5";
|
||||
import PricingComparator from "@/components/pricing-comparator";
|
||||
import { getTranslations } from 'next-intl/server';
|
||||
|
||||
interface PricingPageProps {
|
||||
params: Promise<{ locale: string }>;
|
||||
};
|
||||
|
||||
export default async function PricingPage(props: PricingPageProps) {
|
||||
const params = await props.params;
|
||||
const { locale } = params;
|
||||
const t = await getTranslations('PricingPage');
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="mt-8 flex flex-col gap-16 pb-16">
|
||||
<Pricing5 />
|
||||
|
||||
<Pricing4 />
|
||||
|
||||
<Pricing3 />
|
||||
|
||||
<PricingComparator />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
29
src/app/[locale]/(marketing)/blocks/pricing/page.tsx
Normal file
29
src/app/[locale]/(marketing)/blocks/pricing/page.tsx
Normal file
@ -0,0 +1,29 @@
|
||||
import Pricing3 from "@/components/nsui/pricing3";
|
||||
import Pricing4 from "@/components/nsui/pricing4";
|
||||
import Pricing5 from "@/components/nsui/pricing5";
|
||||
import PricingComparator from "@/components/pricing-comparator";
|
||||
import { getTranslations } from 'next-intl/server';
|
||||
|
||||
interface PricingPageProps {
|
||||
params: Promise<{ locale: string }>;
|
||||
};
|
||||
|
||||
export default async function PricingPage(props: PricingPageProps) {
|
||||
const params = await props.params;
|
||||
const { locale } = params;
|
||||
const t = await getTranslations('PricingPage');
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="mt-8 flex flex-col gap-16 pb-16">
|
||||
<Pricing5 />
|
||||
|
||||
<Pricing4 />
|
||||
|
||||
<Pricing3 />
|
||||
|
||||
<PricingComparator />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
import Pricing3 from "@/components/nsui/pricing3";
|
||||
import Pricing4 from "@/components/nsui/pricing4";
|
||||
import Pricing5 from "@/components/nsui/pricing5";
|
||||
import PricingComparator from "@/components/pricing-comparator";
|
||||
import { getTranslations } from 'next-intl/server';
|
||||
|
||||
interface PricingPageProps {
|
||||
@ -20,6 +21,8 @@ export default async function PricingPage(props: PricingPageProps) {
|
||||
<Pricing4 />
|
||||
|
||||
<Pricing3 />
|
||||
|
||||
<PricingComparator />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
@ -43,7 +43,7 @@ export default function Pricing3() {
|
||||
</Card>
|
||||
|
||||
<Card className="relative">
|
||||
<span className="bg-linear-to-br/increasing absolute inset-x-0 -top-3 mx-auto flex h-6 w-fit items-center rounded-full from-purple-400 to-amber-300 px-3 py-1 text-xs font-medium text-amber-950 ring-1 ring-inset ring-white/20 ring-offset-1 ring-offset-gray-950/5">Popular</span>
|
||||
<span className="absolute inset-x-0 -top-3 mx-auto flex h-6 w-fit items-center rounded-full px-3 py-1 text-xs font-medium bg-primary text-primary-foreground">Popular</span>
|
||||
|
||||
<CardHeader>
|
||||
<CardTitle className="font-medium">Pro</CardTitle>
|
||||
|
182
src/components/pricing-comparator.tsx
Normal file
182
src/components/pricing-comparator.tsx
Normal file
@ -0,0 +1,182 @@
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Cpu, Sparkles } from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
|
||||
const tableData = [
|
||||
{
|
||||
feature: 'Feature 1',
|
||||
free: true,
|
||||
pro: true,
|
||||
startup: true,
|
||||
},
|
||||
{
|
||||
feature: 'Feature 2',
|
||||
free: true,
|
||||
pro: true,
|
||||
startup: true,
|
||||
},
|
||||
{
|
||||
feature: 'Feature 3',
|
||||
free: false,
|
||||
pro: true,
|
||||
startup: true,
|
||||
},
|
||||
{
|
||||
feature: 'Tokens',
|
||||
free: '',
|
||||
pro: '20 Users',
|
||||
startup: 'Unlimited',
|
||||
},
|
||||
{
|
||||
feature: 'Video calls',
|
||||
free: '',
|
||||
pro: '12 Weeks',
|
||||
startup: '56',
|
||||
},
|
||||
{
|
||||
feature: 'Support',
|
||||
free: '',
|
||||
pro: 'Secondes',
|
||||
startup: 'Unlimited',
|
||||
},
|
||||
{
|
||||
feature: 'Security',
|
||||
free: '',
|
||||
pro: '20 Users',
|
||||
startup: 'Unlimited',
|
||||
},
|
||||
]
|
||||
|
||||
/**
|
||||
* https://nsui.irung.me/comparator
|
||||
*/
|
||||
export default function PricingComparator() {
|
||||
return (
|
||||
<section className="py-16">
|
||||
<div className="mx-auto max-w-5xl px-6">
|
||||
<div className="w-full overflow-auto lg:overflow-visible">
|
||||
<table className="w-[200vw] border-separate border-spacing-x-3 md:w-full dark:[--color-muted:var(--color-zinc-900)]">
|
||||
<thead className="bg-background sticky top-0">
|
||||
<tr className="*:py-4 *:text-left *:font-medium">
|
||||
<th className="lg:w-2/5"></th>
|
||||
<th className="space-y-3">
|
||||
<span className="block">Free</span>
|
||||
|
||||
<Button asChild variant="outline" size="sm">
|
||||
<Link href="#">Get Started</Link>
|
||||
</Button>
|
||||
</th>
|
||||
<th className="bg-muted rounded-t-(--radius) space-y-3 px-4">
|
||||
<span className="block">Pro</span>
|
||||
<Button asChild size="sm">
|
||||
<Link href="#">Get Started</Link>
|
||||
</Button>
|
||||
</th>
|
||||
<th className="space-y-3">
|
||||
<span className="block">Startup</span>
|
||||
<Button asChild variant="outline" size="sm">
|
||||
<Link href="#">Get Started</Link>
|
||||
</Button>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="text-caption text-sm">
|
||||
<tr className="*:py-3">
|
||||
<td className="flex items-center gap-2 font-medium">
|
||||
<Cpu className="size-4" />
|
||||
<span>Features</span>
|
||||
</td>
|
||||
<td></td>
|
||||
<td className="bg-muted border-none px-4"></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
{tableData.slice(-4).map((row, index) => (
|
||||
<tr key={index} className="*:border-b *:py-3">
|
||||
<td className="text-muted-foreground">{row.feature}</td>
|
||||
<td>
|
||||
{row.free === true ? (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" className="size-4">
|
||||
<path fillRule="evenodd" d="M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12Zm13.36-1.814a.75.75 0 1 0-1.22-.872l-3.236 4.53L9.53 12.22a.75.75 0 0 0-1.06 1.06l2.25 2.25a.75.75 0 0 0 1.14-.094l3.75-5.25Z" clipRule="evenodd" />
|
||||
</svg>
|
||||
) : (
|
||||
row.free
|
||||
)}
|
||||
</td>
|
||||
<td className="bg-muted border-none px-4">
|
||||
<div className="-mb-3 border-b py-3">
|
||||
{row.pro === true ? (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" className="size-4">
|
||||
<path fillRule="evenodd" d="M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12Zm13.36-1.814a.75.75 0 1 0-1.22-.872l-3.236 4.53L9.53 12.22a.75.75 0 0 0-1.06 1.06l2.25 2.25a.75.75 0 0 0 1.14-.094l3.75-5.25Z" clipRule="evenodd" />
|
||||
</svg>
|
||||
) : (
|
||||
row.pro
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
{row.startup === true ? (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" className="size-4">
|
||||
<path fillRule="evenodd" d="M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12Zm13.36-1.814a.75.75 0 1 0-1.22-.872l-3.236 4.53L9.53 12.22a.75.75 0 0 0-1.06 1.06l2.25 2.25a.75.75 0 0 0 1.14-.094l3.75-5.25Z" clipRule="evenodd" />
|
||||
</svg>
|
||||
) : (
|
||||
row.startup
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
<tr className="*:pb-3 *:pt-8">
|
||||
<td className="flex items-center gap-2 font-medium">
|
||||
<Sparkles className="size-4" />
|
||||
<span>AI Models</span>
|
||||
</td>
|
||||
<td></td>
|
||||
<td className="bg-muted border-none px-4"></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
{tableData.map((row, index) => (
|
||||
<tr key={index} className="*:border-b *:py-3">
|
||||
<td className="text-muted-foreground">{row.feature}</td>
|
||||
<td>
|
||||
{row.free === true ? (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" className="size-4">
|
||||
<path fillRule="evenodd" d="M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12Zm13.36-1.814a.75.75 0 1 0-1.22-.872l-3.236 4.53L9.53 12.22a.75.75 0 0 0-1.06 1.06l2.25 2.25a.75.75 0 0 0 1.14-.094l3.75-5.25Z" clipRule="evenodd" />
|
||||
</svg>
|
||||
) : (
|
||||
row.free
|
||||
)}
|
||||
</td>
|
||||
<td className="bg-muted border-none px-4">
|
||||
<div className="-mb-3 border-b py-3">
|
||||
{row.pro === true ? (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" className="size-4">
|
||||
<path fillRule="evenodd" d="M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12Zm13.36-1.814a.75.75 0 1 0-1.22-.872l-3.236 4.53L9.53 12.22a.75.75 0 0 0-1.06 1.06l2.25 2.25a.75.75 0 0 0 1.14-.094l3.75-5.25Z" clipRule="evenodd" />
|
||||
</svg>
|
||||
) : (
|
||||
row.pro
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
{row.startup === true ? (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" className="size-4">
|
||||
<path fillRule="evenodd" d="M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12Zm13.36-1.814a.75.75 0 1 0-1.22-.872l-3.236 4.53L9.53 12.22a.75.75 0 0 0-1.06 1.06l2.25 2.25a.75.75 0 0 0 1.14-.094l3.75-5.25Z" clipRule="evenodd" />
|
||||
</svg>
|
||||
) : (
|
||||
row.startup
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
<tr className="*:py-6">
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td className="bg-muted rounded-b-(--radius) border-none px-4"></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
@ -12,17 +12,24 @@ import { DashboardIcon } from '@radix-ui/react-icons';
|
||||
import {
|
||||
AudioLinesIcon,
|
||||
BuildingIcon,
|
||||
ChartNoAxesCombinedIcon,
|
||||
CircleDollarSignIcon,
|
||||
CircleHelpIcon,
|
||||
CookieIcon,
|
||||
FileTextIcon,
|
||||
FilmIcon,
|
||||
FlameIcon,
|
||||
ImageIcon,
|
||||
ListChecksIcon,
|
||||
MailboxIcon,
|
||||
MailIcon,
|
||||
NewspaperIcon,
|
||||
RocketIcon,
|
||||
SettingsIcon,
|
||||
ShieldCheckIcon,
|
||||
SquareKanbanIcon,
|
||||
SquarePenIcon
|
||||
SquarePenIcon,
|
||||
WandSparklesIcon
|
||||
} from 'lucide-react';
|
||||
|
||||
type TranslationFunction = (key: string, ...args: any[]) => string;
|
||||
@ -53,47 +60,47 @@ export function createTranslator(t: any): TranslationFunction {
|
||||
export function getMenuLinks(t: TranslationFunction): NestedMenuItem[] {
|
||||
return [
|
||||
{
|
||||
title: t('Marketing.menu.features.title'),
|
||||
title: t('Marketing.navbar.features.title'),
|
||||
href: Routes.Features,
|
||||
external: false
|
||||
},
|
||||
{
|
||||
title: t('Marketing.menu.pricing.title'),
|
||||
title: t('Marketing.navbar.pricing.title'),
|
||||
href: Routes.Pricing,
|
||||
external: false
|
||||
},
|
||||
{
|
||||
title: t('Marketing.menu.blog.title'),
|
||||
title: t('Marketing.navbar.blog.title'),
|
||||
href: Routes.Blog,
|
||||
external: false
|
||||
},
|
||||
{
|
||||
title: t('Marketing.menu.ai.title'),
|
||||
title: t('Marketing.navbar.ai.title'),
|
||||
items: [
|
||||
{
|
||||
title: t('Marketing.menu.ai.items.text.title'),
|
||||
description: t('Marketing.menu.ai.items.text.description'),
|
||||
title: t('Marketing.navbar.ai.items.text.title'),
|
||||
description: t('Marketing.navbar.ai.items.text.description'),
|
||||
icon: <SquarePenIcon className="size-5 shrink-0" />,
|
||||
href: Routes.AIText,
|
||||
external: false
|
||||
},
|
||||
{
|
||||
title: t('Marketing.menu.ai.items.image.title'),
|
||||
description: t('Marketing.menu.ai.items.image.description'),
|
||||
title: t('Marketing.navbar.ai.items.image.title'),
|
||||
description: t('Marketing.navbar.ai.items.image.description'),
|
||||
icon: <ImageIcon className="size-5 shrink-0" />,
|
||||
href: Routes.AIImage,
|
||||
external: false
|
||||
},
|
||||
{
|
||||
title: t('Marketing.menu.ai.items.video.title'),
|
||||
description: t('Marketing.menu.ai.items.video.description'),
|
||||
title: t('Marketing.navbar.ai.items.video.title'),
|
||||
description: t('Marketing.navbar.ai.items.video.description'),
|
||||
icon: <FilmIcon className="size-5 shrink-0" />,
|
||||
href: Routes.AIVideo,
|
||||
external: false
|
||||
},
|
||||
{
|
||||
title: t('Marketing.menu.ai.items.audio.title'),
|
||||
description: t('Marketing.menu.ai.items.audio.description'),
|
||||
title: t('Marketing.navbar.ai.items.audio.title'),
|
||||
description: t('Marketing.navbar.ai.items.audio.description'),
|
||||
icon: <AudioLinesIcon className="size-5 shrink-0" />,
|
||||
href: Routes.AIAudio,
|
||||
external: false
|
||||
@ -101,66 +108,113 @@ export function getMenuLinks(t: TranslationFunction): NestedMenuItem[] {
|
||||
]
|
||||
},
|
||||
{
|
||||
title: t('Marketing.menu.pages.title'),
|
||||
title: t('Marketing.navbar.pages.title'),
|
||||
items: [
|
||||
{
|
||||
title: t('Marketing.menu.pages.items.about.title'),
|
||||
description: t('Marketing.menu.pages.items.about.description'),
|
||||
title: t('Marketing.navbar.pages.items.about.title'),
|
||||
description: t('Marketing.navbar.pages.items.about.description'),
|
||||
icon: <BuildingIcon className="size-5 shrink-0" />,
|
||||
href: Routes.About,
|
||||
external: false
|
||||
},
|
||||
{
|
||||
title: t('Marketing.menu.pages.items.contact.title'),
|
||||
description: t('Marketing.menu.pages.items.contact.description'),
|
||||
title: t('Marketing.navbar.pages.items.contact.title'),
|
||||
description: t('Marketing.navbar.pages.items.contact.description'),
|
||||
icon: <MailIcon className="size-5 shrink-0" />,
|
||||
href: Routes.Contact,
|
||||
external: false
|
||||
},
|
||||
{
|
||||
title: t('Marketing.menu.pages.items.waitlist.title'),
|
||||
description: t('Marketing.menu.pages.items.waitlist.description'),
|
||||
title: t('Marketing.navbar.pages.items.waitlist.title'),
|
||||
description: t('Marketing.navbar.pages.items.waitlist.description'),
|
||||
icon: <MailboxIcon className="size-5 shrink-0" />,
|
||||
href: Routes.Waitlist,
|
||||
external: false
|
||||
},
|
||||
{
|
||||
title: t('Marketing.menu.pages.items.changelog.title'),
|
||||
description: t('Marketing.menu.pages.items.changelog.description'),
|
||||
title: t('Marketing.navbar.pages.items.changelog.title'),
|
||||
description: t('Marketing.navbar.pages.items.changelog.description'),
|
||||
icon: <ListChecksIcon className="size-5 shrink-0" />,
|
||||
href: Routes.Changelog,
|
||||
external: false
|
||||
},
|
||||
{
|
||||
title: t('Marketing.menu.pages.items.roadmap.title'),
|
||||
description: t('Marketing.menu.pages.items.roadmap.description'),
|
||||
title: t('Marketing.navbar.pages.items.roadmap.title'),
|
||||
description: t('Marketing.navbar.pages.items.roadmap.description'),
|
||||
icon: <SquareKanbanIcon className="size-5 shrink-0" />,
|
||||
href: Routes.Roadmap,
|
||||
external: true
|
||||
},
|
||||
{
|
||||
title: t('Marketing.menu.pages.items.cookiePolicy.title'),
|
||||
description: t('Marketing.menu.pages.items.cookiePolicy.description'),
|
||||
title: t('Marketing.navbar.pages.items.cookiePolicy.title'),
|
||||
description: t('Marketing.navbar.pages.items.cookiePolicy.description'),
|
||||
icon: <CookieIcon className="size-5 shrink-0" />,
|
||||
href: Routes.CookiePolicy,
|
||||
external: false
|
||||
},
|
||||
{
|
||||
title: t('Marketing.menu.pages.items.privacyPolicy.title'),
|
||||
description: t('Marketing.menu.pages.items.privacyPolicy.description'),
|
||||
title: t('Marketing.navbar.pages.items.privacyPolicy.title'),
|
||||
description: t('Marketing.navbar.pages.items.privacyPolicy.description'),
|
||||
icon: <ShieldCheckIcon className="size-5 shrink-0" />,
|
||||
href: Routes.PrivacyPolicy,
|
||||
external: false
|
||||
},
|
||||
{
|
||||
title: t('Marketing.menu.pages.items.termsOfService.title'),
|
||||
description: t('Marketing.menu.pages.items.termsOfService.description'),
|
||||
title: t('Marketing.navbar.pages.items.termsOfService.title'),
|
||||
description: t('Marketing.navbar.pages.items.termsOfService.description'),
|
||||
icon: <FileTextIcon className="size-5 shrink-0" />,
|
||||
href: Routes.TermsOfService,
|
||||
external: false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: t('Marketing.navbar.blocks.title'),
|
||||
items: [
|
||||
{
|
||||
title: t('Marketing.navbar.blocks.items.hero.title'),
|
||||
icon: <FlameIcon className="size-5 shrink-0" />,
|
||||
href: Routes.HeroBlocks,
|
||||
external: false
|
||||
},
|
||||
{
|
||||
title: t('Marketing.navbar.blocks.items.pricing.title'),
|
||||
icon: <CircleDollarSignIcon className="size-5 shrink-0" />,
|
||||
href: Routes.PricingBlocks,
|
||||
external: false
|
||||
},
|
||||
{
|
||||
title: t('Marketing.navbar.blocks.items.features.title'),
|
||||
icon: <WandSparklesIcon className="size-5 shrink-0" />,
|
||||
href: Routes.FeaturesBlocks,
|
||||
external: false
|
||||
},
|
||||
{
|
||||
title: t('Marketing.navbar.blocks.items.faq.title'),
|
||||
icon: <CircleHelpIcon className="size-5 shrink-0" />,
|
||||
href: Routes.FAQBlocks,
|
||||
external: false
|
||||
},
|
||||
{
|
||||
title: t('Marketing.navbar.blocks.items.stats.title'),
|
||||
icon: <ChartNoAxesCombinedIcon className="size-5 shrink-0" />,
|
||||
href: Routes.StatsBlocks,
|
||||
external: false
|
||||
},
|
||||
{
|
||||
title: t('Marketing.navbar.blocks.items.callToAction.title'),
|
||||
icon: <RocketIcon className="size-5 shrink-0" />,
|
||||
href: Routes.CallToActionBlocks,
|
||||
external: false
|
||||
},
|
||||
{
|
||||
title: t('Marketing.navbar.blocks.items.content.title'),
|
||||
icon: <NewspaperIcon className="size-5 shrink-0" />,
|
||||
href: Routes.ContentBlocks,
|
||||
external: false
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -97,8 +97,8 @@ export async function getTemplate<T extends Template>({
|
||||
|
||||
// get the subject from the messages
|
||||
const subject =
|
||||
"subject" in messages.mail[template as keyof Messages["mail"]]
|
||||
? messages.mail[template].subject
|
||||
"subject" in messages.Mail[template as keyof Messages["Mail"]]
|
||||
? messages.Mail[template].subject
|
||||
: "";
|
||||
|
||||
const html = await render(email);
|
||||
|
@ -34,6 +34,14 @@ export enum Routes {
|
||||
AIImage = '/dashboard/features/ai-image',
|
||||
AIVideo = '/dashboard/features/ai-video',
|
||||
AIAudio = '/dashboard/features/ai-audio',
|
||||
|
||||
HeroBlocks = '/blocks/hero',
|
||||
PricingBlocks = '/blocks/pricing',
|
||||
FeaturesBlocks = '/blocks/features',
|
||||
FAQBlocks = '/blocks/faq',
|
||||
StatsBlocks = '/blocks/stats',
|
||||
CallToActionBlocks = '/blocks/call-to-action',
|
||||
ContentBlocks = '/blocks/content',
|
||||
}
|
||||
|
||||
/**
|
||||
|
3
src/types/index.d.ts
vendored
3
src/types/index.d.ts
vendored
@ -2,8 +2,7 @@ import type { Icons } from "@/components/icons/icons";
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
/**
|
||||
* utm parameters
|
||||
* https://utmbuilder.com/
|
||||
* site config
|
||||
*/
|
||||
export type SiteConfig = {
|
||||
name: string;
|
||||
|
Loading…
Reference in New Issue
Block a user