refactor: format src/app folder
This commit is contained in:
parent
02bdb93bbd
commit
92ec1b14c5
@ -9,6 +9,7 @@
|
|||||||
"ignoreUnknown": true,
|
"ignoreUnknown": true,
|
||||||
"ignore": [
|
"ignore": [
|
||||||
".next/**",
|
".next/**",
|
||||||
|
".content-collections/**",
|
||||||
"node_modules/**",
|
"node_modules/**",
|
||||||
"dist/**",
|
"dist/**",
|
||||||
"build/**",
|
"build/**",
|
||||||
@ -45,6 +46,7 @@
|
|||||||
},
|
},
|
||||||
"ignore": [
|
"ignore": [
|
||||||
".next/**",
|
".next/**",
|
||||||
|
".content-collections/**",
|
||||||
"node_modules/**",
|
"node_modules/**",
|
||||||
"dist/**",
|
"dist/**",
|
||||||
"build/**",
|
"build/**",
|
||||||
@ -55,7 +57,7 @@
|
|||||||
},
|
},
|
||||||
"javascript": {
|
"javascript": {
|
||||||
"formatter": {
|
"formatter": {
|
||||||
"quoteStyle": "double",
|
"quoteStyle": "single",
|
||||||
"trailingCommas": "es5",
|
"trailingCommas": "es5",
|
||||||
"semicolons": "always"
|
"semicolons": "always"
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { AppSidebar } from "@/components/app-sidebar";
|
import { AppSidebar } from '@/components/app-sidebar';
|
||||||
import {
|
import {
|
||||||
Breadcrumb,
|
Breadcrumb,
|
||||||
BreadcrumbItem,
|
BreadcrumbItem,
|
||||||
@ -6,20 +6,20 @@ import {
|
|||||||
BreadcrumbList,
|
BreadcrumbList,
|
||||||
BreadcrumbPage,
|
BreadcrumbPage,
|
||||||
BreadcrumbSeparator,
|
BreadcrumbSeparator,
|
||||||
} from "@/components/ui/breadcrumb";
|
} from '@/components/ui/breadcrumb';
|
||||||
import { Separator } from "@/components/ui/separator";
|
import { Separator } from '@/components/ui/separator';
|
||||||
import {
|
import {
|
||||||
SidebarInset,
|
SidebarInset,
|
||||||
SidebarProvider,
|
SidebarProvider,
|
||||||
SidebarTrigger,
|
SidebarTrigger,
|
||||||
} from "@/components/ui/sidebar";
|
} from '@/components/ui/sidebar';
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
return (
|
return (
|
||||||
<SidebarProvider>
|
<SidebarProvider>
|
||||||
<AppSidebar />
|
<AppSidebar />
|
||||||
<SidebarInset>
|
<SidebarInset>
|
||||||
<header className="flex h-16 shrink-0 items-center gap-2 transition-[width,height] ease-linear group-has-[[data-collapsible=icon]]/sidebar-wrapper:h-12">
|
<header className="flex h-16 shrink-0 items-center gap-2 transition-[width,height] ease-linear group-has-[[data-collapsible=icon]]/sidebar-wrapper:h-12">
|
||||||
<div className="flex items-center gap-2 px-4">
|
<div className="flex items-center gap-2 px-4">
|
||||||
<SidebarTrigger className="-ml-1" />
|
<SidebarTrigger className="-ml-1" />
|
||||||
<Separator orientation="vertical" className="mr-2 h-4" />
|
<Separator orientation="vertical" className="mr-2 h-4" />
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
import CallToAction3 from "@/components/blocks/call-to-action/call-to-action-3";
|
import CallToAction3 from '@/components/blocks/call-to-action/call-to-action-3';
|
||||||
import Content2 from "@/components/blocks/content/content-2";
|
import Content2 from '@/components/blocks/content/content-2';
|
||||||
import FAQs from "@/components/blocks/faq/faqs";
|
import FAQs from '@/components/blocks/faq/faqs';
|
||||||
import FeaturesSection from "@/components/blocks/features/features-8";
|
import FeaturesSection from '@/components/blocks/features/features-8';
|
||||||
import HeroSection from "@/components/blocks/hero/hero-section-4";
|
import HeroSection from '@/components/blocks/hero/hero-section-4';
|
||||||
import LogoCloud from "@/components/blocks/logo-cloud/logo-cloud";
|
import LogoCloud from '@/components/blocks/logo-cloud/logo-cloud';
|
||||||
import Pricing from "@/components/blocks/pricing/pricing";
|
import Pricing from '@/components/blocks/pricing/pricing';
|
||||||
import StatsSection from "@/components/blocks/stats/stats";
|
import StatsSection from '@/components/blocks/stats/stats';
|
||||||
import { getTranslations } from 'next-intl/server';
|
import { getTranslations } from 'next-intl/server';
|
||||||
|
|
||||||
interface HomePageProps {
|
interface HomePageProps {
|
||||||
params: Promise<{ locale: string }>;
|
params: Promise<{ locale: string }>;
|
||||||
};
|
}
|
||||||
|
|
||||||
export default async function HomePage(props: HomePageProps) {
|
export default async function HomePage(props: HomePageProps) {
|
||||||
const params = await props.params;
|
const params = await props.params;
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from '@/components/ui/button';
|
||||||
import { siteConfig } from "@/config/site";
|
import { siteConfig } from '@/config/site';
|
||||||
import { MailIcon, UserCircleIcon } from "lucide-react";
|
import { MailIcon, UserCircleIcon } from 'lucide-react';
|
||||||
import { getTranslations } from "next-intl/server";
|
import { getTranslations } from 'next-intl/server';
|
||||||
import Image from "next/image";
|
import Image from 'next/image';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* inspired by https://astro-nomy.vercel.app/about
|
* inspired by https://astro-nomy.vercel.app/about
|
||||||
*/
|
*/
|
||||||
export default async function AboutPage() {
|
export default async function AboutPage() {
|
||||||
const t = await getTranslations("AboutPage");
|
const t = await getTranslations('AboutPage');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="space-y-8 pb-16">
|
<section className="space-y-8 pb-16">
|
||||||
@ -31,10 +31,10 @@ export default async function AboutPage() {
|
|||||||
</Avatar>
|
</Avatar>
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-4xl font-heading text-foreground">
|
<h1 className="text-4xl font-heading text-foreground">
|
||||||
{t("authorName")}
|
{t('authorName')}
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-base text-muted-foreground mt-2">
|
<p className="text-base text-muted-foreground mt-2">
|
||||||
{t("authorBio")}
|
{t('authorBio')}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -42,15 +42,13 @@ export default async function AboutPage() {
|
|||||||
{/* introduction */}
|
{/* introduction */}
|
||||||
<div>
|
<div>
|
||||||
<p className="mb-8 text-base text-muted-foreground">
|
<p className="mb-8 text-base text-muted-foreground">
|
||||||
{t("authorIntroduction")}
|
{t('authorIntroduction')}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
<Button className="rounded-lg">
|
<Button className="rounded-lg">
|
||||||
<MailIcon className="mr-1 size-4" />
|
<MailIcon className="mr-1 size-4" />
|
||||||
<a href={`mailto:${siteConfig.mail}`}>
|
<a href={`mailto:${siteConfig.mail}`}>{t('talkWithMe')}</a>
|
||||||
{t("talkWithMe")}
|
|
||||||
</a>
|
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -65,14 +63,17 @@ export default async function AboutPage() {
|
|||||||
{/* Mobile view (1 column) */}
|
{/* Mobile view (1 column) */}
|
||||||
<div className="grid grid-cols-1 gap-4 sm:hidden">
|
<div className="grid grid-cols-1 gap-4 sm:hidden">
|
||||||
{images.map((image, index) => (
|
{images.map((image, index) => (
|
||||||
<div key={index} className="overflow-hidden rounded-xl aspect-[4/3]">
|
<div
|
||||||
|
key={index}
|
||||||
|
className="overflow-hidden rounded-xl aspect-[4/3]"
|
||||||
|
>
|
||||||
<Image
|
<Image
|
||||||
className="w-full h-full object-cover"
|
className="w-full h-full object-cover"
|
||||||
src={image.image}
|
src={image.image}
|
||||||
alt={image.alt}
|
alt={image.alt}
|
||||||
width={800}
|
width={800}
|
||||||
height={900}
|
height={900}
|
||||||
loading={index < 2 ? "eager" : "lazy"}
|
loading={index < 2 ? 'eager' : 'lazy'}
|
||||||
priority={index < 2}
|
priority={index < 2}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -83,14 +84,17 @@ export default async function AboutPage() {
|
|||||||
<div className="hidden sm:grid sm:grid-cols-2 md:hidden gap-4">
|
<div className="hidden sm:grid sm:grid-cols-2 md:hidden gap-4">
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{images.slice(0, 4).map((image, index) => (
|
{images.slice(0, 4).map((image, index) => (
|
||||||
<div key={index} className="overflow-hidden rounded-xl aspect-[4/3]">
|
<div
|
||||||
|
key={index}
|
||||||
|
className="overflow-hidden rounded-xl aspect-[4/3]"
|
||||||
|
>
|
||||||
<Image
|
<Image
|
||||||
className="w-full h-full object-cover"
|
className="w-full h-full object-cover"
|
||||||
src={image.image}
|
src={image.image}
|
||||||
alt={image.alt}
|
alt={image.alt}
|
||||||
width={800}
|
width={800}
|
||||||
height={900}
|
height={900}
|
||||||
loading={index < 2 ? "eager" : "lazy"}
|
loading={index < 2 ? 'eager' : 'lazy'}
|
||||||
priority={index < 2}
|
priority={index < 2}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -98,14 +102,17 @@ export default async function AboutPage() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{images.slice(4, 8).map((image, index) => (
|
{images.slice(4, 8).map((image, index) => (
|
||||||
<div key={index} className="overflow-hidden rounded-xl aspect-[4/3]">
|
<div
|
||||||
|
key={index}
|
||||||
|
className="overflow-hidden rounded-xl aspect-[4/3]"
|
||||||
|
>
|
||||||
<Image
|
<Image
|
||||||
className="w-full h-full object-cover"
|
className="w-full h-full object-cover"
|
||||||
src={image.image}
|
src={image.image}
|
||||||
alt={image.alt}
|
alt={image.alt}
|
||||||
width={800}
|
width={800}
|
||||||
height={900}
|
height={900}
|
||||||
loading={index < 2 ? "eager" : "lazy"}
|
loading={index < 2 ? 'eager' : 'lazy'}
|
||||||
priority={index < 1}
|
priority={index < 1}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -237,50 +244,50 @@ const images: ImagesProps[] = [
|
|||||||
// first column
|
// first column
|
||||||
{
|
{
|
||||||
image:
|
image:
|
||||||
"https://images.pexels.com/photos/15372903/pexels-photo-15372903/free-photo-of-computer-setup-with-big-monitor-screen.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
|
'https://images.pexels.com/photos/15372903/pexels-photo-15372903/free-photo-of-computer-setup-with-big-monitor-screen.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1',
|
||||||
alt: "setup desktop",
|
alt: 'setup desktop',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
image:
|
image:
|
||||||
"https://images.pexels.com/photos/1049317/pexels-photo-1049317.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
|
'https://images.pexels.com/photos/1049317/pexels-photo-1049317.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1',
|
||||||
alt: "friends smiles",
|
alt: 'friends smiles',
|
||||||
},
|
},
|
||||||
// second column
|
// second column
|
||||||
{
|
{
|
||||||
image:
|
image:
|
||||||
"https://images.pexels.com/photos/3712095/pexels-photo-3712095.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
|
'https://images.pexels.com/photos/3712095/pexels-photo-3712095.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1',
|
||||||
alt: "grey cat",
|
alt: 'grey cat',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
image:
|
image:
|
||||||
"https://images.pexels.com/photos/9293249/pexels-photo-9293249.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
|
'https://images.pexels.com/photos/9293249/pexels-photo-9293249.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1',
|
||||||
alt: "home building",
|
alt: 'home building',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
image:
|
image:
|
||||||
"https://images.pexels.com/photos/375467/pexels-photo-375467.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
|
'https://images.pexels.com/photos/375467/pexels-photo-375467.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1',
|
||||||
alt: "pizza laptop",
|
alt: 'pizza laptop',
|
||||||
},
|
},
|
||||||
// third column
|
// third column
|
||||||
{
|
{
|
||||||
image:
|
image:
|
||||||
"https://images.pexels.com/photos/1230302/pexels-photo-1230302.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
|
'https://images.pexels.com/photos/1230302/pexels-photo-1230302.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1',
|
||||||
alt: "hike and sunset",
|
alt: 'hike and sunset',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
image:
|
image:
|
||||||
"https://images.pexels.com/photos/5500779/pexels-photo-5500779.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
|
'https://images.pexels.com/photos/5500779/pexels-photo-5500779.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1',
|
||||||
alt: "chinese lantern",
|
alt: 'chinese lantern',
|
||||||
},
|
},
|
||||||
// fourth column
|
// fourth column
|
||||||
{
|
{
|
||||||
image:
|
image:
|
||||||
"https://images.pexels.com/photos/2090644/pexels-photo-2090644.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
|
'https://images.pexels.com/photos/2090644/pexels-photo-2090644.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1',
|
||||||
alt: "the great wheel",
|
alt: 'the great wheel',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
image:
|
image:
|
||||||
"https://images.pexels.com/photos/7418632/pexels-photo-7418632.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
|
'https://images.pexels.com/photos/7418632/pexels-photo-7418632.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1',
|
||||||
alt: "dalmatian",
|
alt: 'dalmatian',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@ -25,8 +25,8 @@ export async function generateMetadata(
|
|||||||
title: 'Changelog',
|
title: 'Changelog',
|
||||||
description: 'Track all updates and improvements to our platform',
|
description: 'Track all updates and improvements to our platform',
|
||||||
type: 'article',
|
type: 'article',
|
||||||
url: `${getBaseUrl()}/changelog`
|
url: `${getBaseUrl()}/changelog`,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
"use client";
|
'use client';
|
||||||
|
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Card } from '@/components/ui/card';
|
import { Card } from '@/components/ui/card';
|
||||||
@ -35,23 +35,17 @@ export default function ContactPage() {
|
|||||||
|
|
||||||
<form action="" className="mt-8 space-y-4">
|
<form action="" className="mt-8 space-y-4">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="name">
|
<Label htmlFor="name">{t('name')}</Label>
|
||||||
{t('name')}
|
|
||||||
</Label>
|
|
||||||
<Input type="text" id="name" required />
|
<Input type="text" id="name" required />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="email">
|
<Label htmlFor="email">{t('email')}</Label>
|
||||||
{t('email')}
|
|
||||||
</Label>
|
|
||||||
<Input type="email" id="email" required />
|
<Input type="email" id="email" required />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="msg">
|
<Label htmlFor="msg">{t('message')}</Label>
|
||||||
{t('message')}
|
|
||||||
</Label>
|
|
||||||
<Textarea id="msg" rows={3} />
|
<Textarea id="msg" rows={3} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -61,5 +55,5 @@ export default function ContactPage() {
|
|||||||
</form>
|
</form>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ export async function generateMetadata(
|
|||||||
|
|
||||||
const locale = params.locale as string;
|
const locale = params.locale as string;
|
||||||
const page = await getCustomPage('cookie-policy', locale);
|
const page = await getCustomPage('cookie-policy', locale);
|
||||||
|
|
||||||
if (!page) {
|
if (!page) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@ -29,8 +29,8 @@ export async function generateMetadata(
|
|||||||
title: page.title,
|
title: page.title,
|
||||||
description: page.description,
|
description: page.description,
|
||||||
type: 'article',
|
type: 'article',
|
||||||
url: `${getBaseUrl()}/cookie-policy`
|
url: `${getBaseUrl()}/cookie-policy`,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,7 +42,7 @@ export default async function CookiePolicyPage(props: NextPageProps) {
|
|||||||
|
|
||||||
const locale = params.locale as string;
|
const locale = params.locale as string;
|
||||||
const page = await getCustomPage('cookie-policy', locale);
|
const page = await getCustomPage('cookie-policy', locale);
|
||||||
|
|
||||||
if (!page) {
|
if (!page) {
|
||||||
notFound();
|
notFound();
|
||||||
}
|
}
|
||||||
@ -55,4 +55,4 @@ export default async function CookiePolicyPage(props: NextPageProps) {
|
|||||||
content={page.body.code}
|
content={page.body.code}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,7 @@ import { PropsWithChildren } from 'react';
|
|||||||
export default function LegalLayout({ children }: PropsWithChildren) {
|
export default function LegalLayout({ children }: PropsWithChildren) {
|
||||||
return (
|
return (
|
||||||
<Container className="py-16 px-4">
|
<Container className="py-16 px-4">
|
||||||
<div className="mx-auto">
|
<div className="mx-auto">{children}</div>
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ export async function generateMetadata(
|
|||||||
|
|
||||||
const locale = params.locale as string;
|
const locale = params.locale as string;
|
||||||
const page = await getCustomPage('privacy-policy', locale);
|
const page = await getCustomPage('privacy-policy', locale);
|
||||||
|
|
||||||
if (!page) {
|
if (!page) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@ -29,8 +29,8 @@ export async function generateMetadata(
|
|||||||
title: page.title,
|
title: page.title,
|
||||||
description: page.description,
|
description: page.description,
|
||||||
type: 'article',
|
type: 'article',
|
||||||
url: `${getBaseUrl()}/privacy-policy`
|
url: `${getBaseUrl()}/privacy-policy`,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,7 +42,7 @@ export default async function PrivacyPolicyPage(props: NextPageProps) {
|
|||||||
|
|
||||||
const locale = params.locale as string;
|
const locale = params.locale as string;
|
||||||
const page = await getCustomPage('privacy-policy', locale);
|
const page = await getCustomPage('privacy-policy', locale);
|
||||||
|
|
||||||
if (!page) {
|
if (!page) {
|
||||||
notFound();
|
notFound();
|
||||||
}
|
}
|
||||||
@ -55,4 +55,4 @@ export default async function PrivacyPolicyPage(props: NextPageProps) {
|
|||||||
content={page.body.code}
|
content={page.body.code}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ export async function generateMetadata(
|
|||||||
|
|
||||||
const locale = params.locale as string;
|
const locale = params.locale as string;
|
||||||
const page = await getCustomPage('terms-of-service', locale);
|
const page = await getCustomPage('terms-of-service', locale);
|
||||||
|
|
||||||
if (!page) {
|
if (!page) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@ -29,8 +29,8 @@ export async function generateMetadata(
|
|||||||
title: page.title,
|
title: page.title,
|
||||||
description: page.description,
|
description: page.description,
|
||||||
type: 'article',
|
type: 'article',
|
||||||
url: `${getBaseUrl()}/terms-of-service`
|
url: `${getBaseUrl()}/terms-of-service`,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,7 +42,7 @@ export default async function TermsOfServicePage(props: NextPageProps) {
|
|||||||
|
|
||||||
const locale = params.locale as string;
|
const locale = params.locale as string;
|
||||||
const page = await getCustomPage('terms-of-service', locale);
|
const page = await getCustomPage('terms-of-service', locale);
|
||||||
|
|
||||||
if (!page) {
|
if (!page) {
|
||||||
notFound();
|
notFound();
|
||||||
}
|
}
|
||||||
@ -55,4 +55,4 @@ export default async function TermsOfServicePage(props: NextPageProps) {
|
|||||||
content={page.body.code}
|
content={page.body.code}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
"use client";
|
'use client';
|
||||||
|
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Card } from '@/components/ui/card';
|
import { Card } from '@/components/ui/card';
|
||||||
@ -7,7 +7,7 @@ import { Label } from '@/components/ui/label';
|
|||||||
import { useTranslations } from 'next-intl';
|
import { useTranslations } from 'next-intl';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export default function WaitlistPage() {
|
export default function WaitlistPage() {
|
||||||
const t = useTranslations('WaitlistPage');
|
const t = useTranslations('WaitlistPage');
|
||||||
@ -34,9 +34,7 @@ export default function WaitlistPage() {
|
|||||||
|
|
||||||
<form action="" className="mt-8 space-y-4">
|
<form action="" className="mt-8 space-y-4">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="email">
|
<Label htmlFor="email">{t('email')}</Label>
|
||||||
{t('email')}
|
|
||||||
</Label>
|
|
||||||
<Input type="email" id="email" required />
|
<Input type="email" id="email" required />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -46,5 +44,5 @@ export default function WaitlistPage() {
|
|||||||
</form>
|
</form>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import CallToAction from "@/components/blocks/call-to-action/call-to-action";
|
import CallToAction from '@/components/blocks/call-to-action/call-to-action';
|
||||||
import CallToAction2 from "@/components/blocks/call-to-action/call-to-action-2";
|
import CallToAction2 from '@/components/blocks/call-to-action/call-to-action-2';
|
||||||
import CallToAction3 from "@/components/blocks/call-to-action/call-to-action-3";
|
import CallToAction3 from '@/components/blocks/call-to-action/call-to-action-3';
|
||||||
|
|
||||||
interface CallToActionPageProps {
|
interface CallToActionPageProps {
|
||||||
params: Promise<{ locale: string }>;
|
params: Promise<{ locale: string }>;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* https://nsui.irung.me/call-to-action
|
* https://nsui.irung.me/call-to-action
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import Content from "@/components/blocks/content/content";
|
import Content from '@/components/blocks/content/content';
|
||||||
import Content2 from "@/components/blocks/content/content-2";
|
import Content2 from '@/components/blocks/content/content-2';
|
||||||
import Content3 from "@/components/blocks/content/content-3";
|
import Content3 from '@/components/blocks/content/content-3';
|
||||||
import Content4 from "@/components/blocks/content/content-4";
|
import Content4 from '@/components/blocks/content/content-4';
|
||||||
import Content5 from "@/components/blocks/content/content-5";
|
import Content5 from '@/components/blocks/content/content-5';
|
||||||
import Content6 from "@/components/blocks/content/content-6";
|
import Content6 from '@/components/blocks/content/content-6';
|
||||||
|
|
||||||
interface ContentPageProps {
|
interface ContentPageProps {
|
||||||
params: Promise<{ locale: string }>;
|
params: Promise<{ locale: string }>;
|
||||||
};
|
}
|
||||||
|
|
||||||
export default async function ContentPage(props: ContentPageProps) {
|
export default async function ContentPage(props: ContentPageProps) {
|
||||||
const params = await props.params;
|
const params = await props.params;
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import FAQs from "@/components/blocks/faq/faqs";
|
import FAQs from '@/components/blocks/faq/faqs';
|
||||||
|
|
||||||
interface FAQPageProps {
|
interface FAQPageProps {
|
||||||
params: Promise<{ locale: string }>;
|
params: Promise<{ locale: string }>;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* https://nsui.irung.me/faqs
|
* https://nsui.irung.me/faqs
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import Features from "@/components/blocks/features/features";
|
import Features from '@/components/blocks/features/features';
|
||||||
import Features2 from "@/components/blocks/features/features-2";
|
import Features2 from '@/components/blocks/features/features-2';
|
||||||
import Features4 from "@/components/blocks/features/features-4";
|
import Features4 from '@/components/blocks/features/features-4';
|
||||||
import Features5 from "@/components/blocks/features/features-5";
|
import Features5 from '@/components/blocks/features/features-5';
|
||||||
import Features6 from "@/components/blocks/features/features-6";
|
import Features6 from '@/components/blocks/features/features-6';
|
||||||
import Features7 from "@/components/blocks/features/features-7";
|
import Features7 from '@/components/blocks/features/features-7';
|
||||||
import Features8 from "@/components/blocks/features/features-8";
|
import Features8 from '@/components/blocks/features/features-8';
|
||||||
import Features9 from "@/components/blocks/features/features-9";
|
import Features9 from '@/components/blocks/features/features-9';
|
||||||
|
|
||||||
interface FeaturesPageProps {
|
interface FeaturesPageProps {
|
||||||
params: Promise<{ locale: string }>;
|
params: Promise<{ locale: string }>;
|
||||||
};
|
}
|
||||||
|
|
||||||
export default async function FeaturesPage(props: FeaturesPageProps) {
|
export default async function FeaturesPage(props: FeaturesPageProps) {
|
||||||
const params = await props.params;
|
const params = await props.params;
|
||||||
@ -18,19 +18,19 @@ export default async function FeaturesPage(props: FeaturesPageProps) {
|
|||||||
<>
|
<>
|
||||||
<div className="mt-8 flex flex-col gap-16 pb-16">
|
<div className="mt-8 flex flex-col gap-16 pb-16">
|
||||||
<Features />
|
<Features />
|
||||||
|
|
||||||
<Features2 />
|
<Features2 />
|
||||||
|
|
||||||
<Features4 />
|
<Features4 />
|
||||||
|
|
||||||
<Features5 />
|
<Features5 />
|
||||||
|
|
||||||
<Features6 />
|
<Features6 />
|
||||||
|
|
||||||
<Features7 />
|
<Features7 />
|
||||||
|
|
||||||
<Features8 />
|
<Features8 />
|
||||||
|
|
||||||
<Features9 />
|
<Features9 />
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import HeroSection from "@/components/blocks/hero/hero-section";
|
import HeroSection from '@/components/blocks/hero/hero-section';
|
||||||
import HeroSection2 from "@/components/blocks/hero/hero-section-2";
|
import HeroSection2 from '@/components/blocks/hero/hero-section-2';
|
||||||
import HeroSection3 from "@/components/blocks/hero/hero-section-3";
|
import HeroSection3 from '@/components/blocks/hero/hero-section-3';
|
||||||
import HeroSection4 from "@/components/blocks/hero/hero-section-4";
|
import HeroSection4 from '@/components/blocks/hero/hero-section-4';
|
||||||
|
|
||||||
interface HeroPageProps {
|
interface HeroPageProps {
|
||||||
params: Promise<{ locale: string }>;
|
params: Promise<{ locale: string }>;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* https://nsui.irung.me/hero-section
|
* https://nsui.irung.me/hero-section
|
||||||
@ -19,7 +19,7 @@ export default async function HeroPage(props: HeroPageProps) {
|
|||||||
<HeroSection />
|
<HeroSection />
|
||||||
|
|
||||||
<HeroSection2 />
|
<HeroSection2 />
|
||||||
|
|
||||||
<HeroSection3 />
|
<HeroSection3 />
|
||||||
|
|
||||||
<HeroSection4 />
|
<HeroSection4 />
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import Pricing3 from "@/components/blocks/pricing/pricing-3";
|
import Pricing3 from '@/components/blocks/pricing/pricing-3';
|
||||||
import Pricing4 from "@/components/blocks/pricing/pricing-4";
|
import Pricing4 from '@/components/blocks/pricing/pricing-4';
|
||||||
import Pricing5 from "@/components/blocks/pricing/pricing-5";
|
import Pricing5 from '@/components/blocks/pricing/pricing-5';
|
||||||
import PricingComparator from "@/components/pricing-comparator";
|
import PricingComparator from '@/components/pricing-comparator';
|
||||||
import { getTranslations } from 'next-intl/server';
|
import { getTranslations } from 'next-intl/server';
|
||||||
|
|
||||||
interface PricingPageProps {
|
interface PricingPageProps {
|
||||||
params: Promise<{ locale: string }>;
|
params: Promise<{ locale: string }>;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* https://nsui.irung.me/pricing
|
* https://nsui.irung.me/pricing
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import Stats from "@/components/blocks/stats/stats";
|
import Stats from '@/components/blocks/stats/stats';
|
||||||
import Stats2 from "@/components/blocks/stats/stats-2";
|
import Stats2 from '@/components/blocks/stats/stats-2';
|
||||||
import Stats3 from "@/components/blocks/stats/stats-3";
|
import Stats3 from '@/components/blocks/stats/stats-3';
|
||||||
import Stats4 from "@/components/blocks/stats/stats-4";
|
import Stats4 from '@/components/blocks/stats/stats-4';
|
||||||
|
|
||||||
interface StatsPageProps {
|
interface StatsPageProps {
|
||||||
params: Promise<{ locale: string }>;
|
params: Promise<{ locale: string }>;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* https://nsui.irung.me/stats
|
* https://nsui.irung.me/stats
|
||||||
@ -17,11 +17,11 @@ export default async function StatsPage(props: StatsPageProps) {
|
|||||||
<>
|
<>
|
||||||
<div className="mt-8 flex flex-col gap-16 pb-16">
|
<div className="mt-8 flex flex-col gap-16 pb-16">
|
||||||
<Stats />
|
<Stats />
|
||||||
|
|
||||||
<Stats2 />
|
<Stats2 />
|
||||||
|
|
||||||
<Stats3 />
|
<Stats3 />
|
||||||
|
|
||||||
<Stats4 />
|
<Stats4 />
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import Testimonials from "@/components/blocks/testimonials/testimonials";
|
import Testimonials from '@/components/blocks/testimonials/testimonials';
|
||||||
import Testimonials2 from "@/components/blocks/testimonials/testimonials-2";
|
import Testimonials2 from '@/components/blocks/testimonials/testimonials-2';
|
||||||
import Testimonials4 from "@/components/blocks/testimonials/testimonials-4";
|
import Testimonials4 from '@/components/blocks/testimonials/testimonials-4';
|
||||||
import Testimonials5 from "@/components/blocks/testimonials/testimonials-5";
|
import Testimonials5 from '@/components/blocks/testimonials/testimonials-5';
|
||||||
import Testimonials6 from "@/components/blocks/testimonials/testimonials-6";
|
import Testimonials6 from '@/components/blocks/testimonials/testimonials-6';
|
||||||
|
|
||||||
interface TestimonialsPageProps {
|
interface TestimonialsPageProps {
|
||||||
params: Promise<{ locale: string }>;
|
params: Promise<{ locale: string }>;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* https://nsui.irung.me/testimonials
|
* https://nsui.irung.me/testimonials
|
||||||
@ -20,11 +20,11 @@ export default async function TestimonialsPage(props: TestimonialsPageProps) {
|
|||||||
<Testimonials />
|
<Testimonials />
|
||||||
|
|
||||||
<Testimonials2 />
|
<Testimonials2 />
|
||||||
|
|
||||||
<Testimonials4 />
|
<Testimonials4 />
|
||||||
|
|
||||||
<Testimonials5 />
|
<Testimonials5 />
|
||||||
|
|
||||||
<Testimonials6 />
|
<Testimonials6 />
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import BlogGrid from "@/components/blog/blog-grid";
|
import BlogGrid from '@/components/blog/blog-grid';
|
||||||
import EmptyGrid from "@/components/shared/empty-grid";
|
import EmptyGrid from '@/components/shared/empty-grid';
|
||||||
import CustomPagination from "@/components/shared/pagination";
|
import CustomPagination from '@/components/shared/pagination';
|
||||||
import { POSTS_PER_PAGE } from "@/lib/constants";
|
import { POSTS_PER_PAGE } from '@/lib/constants';
|
||||||
import { allPosts, allCategories } from "content-collections";
|
import { allPosts, allCategories } from 'content-collections';
|
||||||
import { siteConfig } from "@/config/site";
|
import { siteConfig } from '@/config/site';
|
||||||
import { constructMetadata } from "@/lib/metadata";
|
import { constructMetadata } from '@/lib/metadata';
|
||||||
import type { Metadata } from "next";
|
import type { Metadata } from 'next';
|
||||||
import { NextPageProps } from "@/types/next-page-props";
|
import { NextPageProps } from '@/types/next-page-props';
|
||||||
|
|
||||||
export async function generateMetadata({
|
export async function generateMetadata({
|
||||||
params,
|
params,
|
||||||
@ -23,15 +23,15 @@ export async function generateMetadata({
|
|||||||
|
|
||||||
if (!category) {
|
if (!category) {
|
||||||
console.warn(
|
console.warn(
|
||||||
`generateMetadata, category not found for slug: ${slug}, locale: ${locale}`,
|
`generateMetadata, category not found for slug: ${slug}, locale: ${locale}`
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ogImageUrl = new URL(`${siteConfig.url}/api/og`);
|
const ogImageUrl = new URL(`${siteConfig.url}/api/og`);
|
||||||
ogImageUrl.searchParams.append("title", category.name);
|
ogImageUrl.searchParams.append('title', category.name);
|
||||||
ogImageUrl.searchParams.append("description", category.description || "");
|
ogImageUrl.searchParams.append('description', category.description || '');
|
||||||
ogImageUrl.searchParams.append("type", "Blog Category");
|
ogImageUrl.searchParams.append('type', 'Blog Category');
|
||||||
|
|
||||||
return constructMetadata({
|
return constructMetadata({
|
||||||
title: `${category.name}`,
|
title: `${category.name}`,
|
||||||
@ -48,7 +48,7 @@ export default async function BlogCategoryPage({
|
|||||||
const resolvedParams = await params;
|
const resolvedParams = await params;
|
||||||
const { slug, locale } = resolvedParams;
|
const { slug, locale } = resolvedParams;
|
||||||
const resolvedSearchParams = await searchParams;
|
const resolvedSearchParams = await searchParams;
|
||||||
const { page } = resolvedSearchParams as { [key: string]: string } || {};
|
const { page } = (resolvedSearchParams as { [key: string]: string }) || {};
|
||||||
const currentPage = page ? Number(page) : 1;
|
const currentPage = page ? Number(page) : 1;
|
||||||
const startIndex = (currentPage - 1) * POSTS_PER_PAGE;
|
const startIndex = (currentPage - 1) * POSTS_PER_PAGE;
|
||||||
const endIndex = startIndex + POSTS_PER_PAGE;
|
const endIndex = startIndex + POSTS_PER_PAGE;
|
||||||
@ -59,16 +59,16 @@ export default async function BlogCategoryPage({
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Filter posts by category and locale
|
// Filter posts by category and locale
|
||||||
const filteredPosts = allPosts.filter(
|
const filteredPosts = allPosts.filter((post) => {
|
||||||
(post) => {
|
if (!post.published || post.locale !== locale) {
|
||||||
if (!post.published || post.locale !== locale) {
|
return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if any of the post's categories match the current category slug
|
|
||||||
return post.categories.some(category => category && category.slug === slug);
|
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
// Check if any of the post's categories match the current category slug
|
||||||
|
return post.categories.some(
|
||||||
|
(category) => category && category.slug === slug
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
// Sort posts by date (newest first)
|
// Sort posts by date (newest first)
|
||||||
const sortedPosts = [...filteredPosts].sort(
|
const sortedPosts = [...filteredPosts].sort(
|
||||||
|
@ -8,35 +8,33 @@ import { NextPageProps } from '@/types/next-page-props';
|
|||||||
|
|
||||||
interface BlogListLayoutProps extends PropsWithChildren, NextPageProps {}
|
interface BlogListLayoutProps extends PropsWithChildren, NextPageProps {}
|
||||||
|
|
||||||
export default async function BlogListLayout({
|
export default async function BlogListLayout({
|
||||||
children,
|
children,
|
||||||
params
|
params,
|
||||||
}: BlogListLayoutProps) {
|
}: BlogListLayoutProps) {
|
||||||
const resolvedParams = await params;
|
const resolvedParams = await params;
|
||||||
const { locale } = resolvedParams;
|
const { locale } = resolvedParams;
|
||||||
const t = await getTranslations("BlogPage");
|
const t = await getTranslations('BlogPage');
|
||||||
|
|
||||||
// Filter categories by locale
|
// Filter categories by locale
|
||||||
// console.log("allCategories", allCategories);
|
// console.log("allCategories", allCategories);
|
||||||
const categoryList = allCategories.filter(
|
const categoryList = allCategories.filter(
|
||||||
category => category.locale === locale
|
(category) => category.locale === locale
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mb-16">
|
<div className="mb-16">
|
||||||
<div className="mt-8 w-full flex flex-col items-center justify-center gap-8">
|
<div className="mt-8 w-full flex flex-col items-center justify-center gap-8">
|
||||||
<HeaderSection
|
<HeaderSection
|
||||||
titleAs="h2"
|
titleAs="h2"
|
||||||
title={t("title")}
|
title={t('title')}
|
||||||
subtitle={t("subtitle")}
|
subtitle={t('subtitle')}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<BlogCategoryFilter categoryList={categoryList} />
|
<BlogCategoryFilter categoryList={categoryList} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Container className="mt-8 px-4">
|
<Container className="mt-8 px-4">{children}</Container>
|
||||||
{children}
|
|
||||||
</Container>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { BlogGridSkeleton } from "@/components/blog/blog-grid";
|
import { BlogGridSkeleton } from '@/components/blog/blog-grid';
|
||||||
|
|
||||||
export default function Loading() {
|
export default function Loading() {
|
||||||
return <BlogGridSkeleton />;
|
return <BlogGridSkeleton />;
|
||||||
|
@ -15,12 +15,12 @@ export async function generateMetadata(): Promise<Metadata> {
|
|||||||
|
|
||||||
export default async function BlogPage({
|
export default async function BlogPage({
|
||||||
params,
|
params,
|
||||||
searchParams
|
searchParams,
|
||||||
}: NextPageProps) {
|
}: NextPageProps) {
|
||||||
const resolvedParams = await params;
|
const resolvedParams = await params;
|
||||||
const { locale } = resolvedParams;
|
const { locale } = resolvedParams;
|
||||||
const resolvedSearchParams = await searchParams;
|
const resolvedSearchParams = await searchParams;
|
||||||
const { page } = resolvedSearchParams as { [key: string]: string } || {};
|
const { page } = (resolvedSearchParams as { [key: string]: string }) || {};
|
||||||
const currentPage = page ? Number(page) : 1;
|
const currentPage = page ? Number(page) : 1;
|
||||||
const startIndex = (currentPage - 1) * POSTS_PER_PAGE;
|
const startIndex = (currentPage - 1) * POSTS_PER_PAGE;
|
||||||
const endIndex = startIndex + POSTS_PER_PAGE;
|
const endIndex = startIndex + POSTS_PER_PAGE;
|
||||||
@ -31,9 +31,10 @@ export default async function BlogPage({
|
|||||||
);
|
);
|
||||||
|
|
||||||
// If no posts found for the current locale, show all published posts
|
// If no posts found for the current locale, show all published posts
|
||||||
const filteredPosts = localePosts.length > 0
|
const filteredPosts =
|
||||||
? localePosts
|
localePosts.length > 0
|
||||||
: allPosts.filter((post) => post.published);
|
? localePosts
|
||||||
|
: allPosts.filter((post) => post.published);
|
||||||
|
|
||||||
// Sort posts by date (newest first)
|
// Sort posts by date (newest first)
|
||||||
const sortedPosts = [...filteredPosts].sort(
|
const sortedPosts = [...filteredPosts].sort(
|
||||||
@ -67,4 +68,4 @@ export default async function BlogPage({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,7 @@ import { PropsWithChildren } from 'react';
|
|||||||
export default function BlogPostLayout({ children }: PropsWithChildren) {
|
export default function BlogPostLayout({ children }: PropsWithChildren) {
|
||||||
return (
|
return (
|
||||||
<Container className="py-8 px-4">
|
<Container className="py-8 px-4">
|
||||||
<div className="mx-auto">
|
<div className="mx-auto">{children}</div>
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -19,13 +19,13 @@ import '@/styles/mdx.css';
|
|||||||
* Gets the blog post from the params
|
* Gets the blog post from the params
|
||||||
* @param props - The props of the page
|
* @param props - The props of the page
|
||||||
* @returns The blog post
|
* @returns The blog post
|
||||||
*
|
*
|
||||||
* How it works:
|
* How it works:
|
||||||
* 1. /[locale]/blog/first-post:
|
* 1. /[locale]/blog/first-post:
|
||||||
* params.slug = ["first-post"]
|
* params.slug = ["first-post"]
|
||||||
* slug becomes "first-post" after join('/')
|
* slug becomes "first-post" after join('/')
|
||||||
* Matches post where slugAsParams === "first-post" AND locale === params.locale
|
* Matches post where slugAsParams === "first-post" AND locale === params.locale
|
||||||
*
|
*
|
||||||
* 2. /[locale]/blog/2023/year-review:
|
* 2. /[locale]/blog/2023/year-review:
|
||||||
* params.slug = ["2023", "year-review"]
|
* params.slug = ["2023", "year-review"]
|
||||||
* slug becomes "2023/year-review" after join('/')
|
* slug becomes "2023/year-review" after join('/')
|
||||||
@ -44,7 +44,8 @@ async function getBlogPostFromParams(props: NextPageProps) {
|
|||||||
// Find post with matching slug and locale
|
// Find post with matching slug and locale
|
||||||
const post = allPosts.find(
|
const post = allPosts.find(
|
||||||
(post) =>
|
(post) =>
|
||||||
(post.slugAsParams === slug || (!slug && post.slugAsParams === 'index')) &&
|
(post.slugAsParams === slug ||
|
||||||
|
(!slug && post.slugAsParams === 'index')) &&
|
||||||
post.locale === locale
|
post.locale === locale
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -52,7 +53,7 @@ async function getBlogPostFromParams(props: NextPageProps) {
|
|||||||
// If no post found with the current locale, try to find one with the default locale
|
// If no post found with the current locale, try to find one with the default locale
|
||||||
const defaultPost = allPosts.find(
|
const defaultPost = allPosts.find(
|
||||||
(post) =>
|
(post) =>
|
||||||
(post.slugAsParams === slug || (!slug && post.slugAsParams === 'index'))
|
post.slugAsParams === slug || (!slug && post.slugAsParams === 'index')
|
||||||
);
|
);
|
||||||
|
|
||||||
return defaultPost;
|
return defaultPost;
|
||||||
@ -76,8 +77,8 @@ export async function generateMetadata(
|
|||||||
title: post.title,
|
title: post.title,
|
||||||
description: post.description,
|
description: post.description,
|
||||||
type: 'article',
|
type: 'article',
|
||||||
url: `${getBaseUrl()}${post.slug}`
|
url: `${getBaseUrl()}${post.slug}`,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,7 +91,7 @@ export default async function BlogPostPage(props: NextPageProps) {
|
|||||||
const publishDate = post.date;
|
const publishDate = post.date;
|
||||||
const date = getLocaleDate(publishDate);
|
const date = getLocaleDate(publishDate);
|
||||||
const toc = await getTableOfContents(post.content);
|
const toc = await getTableOfContents(post.content);
|
||||||
const t = await getTranslations("BlogPage");
|
const t = await getTranslations('BlogPage');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-8">
|
<div className="flex flex-col gap-8">
|
||||||
@ -105,8 +106,8 @@ export default async function BlogPostPage(props: NextPageProps) {
|
|||||||
{post.image && (
|
{post.image && (
|
||||||
<Image
|
<Image
|
||||||
src={post.image}
|
src={post.image}
|
||||||
alt={post.title || "image for blog post"}
|
alt={post.title || 'image for blog post'}
|
||||||
title={post.title || "image for blog post"}
|
title={post.title || 'image for blog post'}
|
||||||
loading="eager"
|
loading="eager"
|
||||||
fill
|
fill
|
||||||
className="object-cover"
|
className="object-cover"
|
||||||
@ -150,7 +151,7 @@ export default async function BlogPostPage(props: NextPageProps) {
|
|||||||
<div className="space-y-4 lg:sticky lg:top-24">
|
<div className="space-y-4 lg:sticky lg:top-24">
|
||||||
{/* author info */}
|
{/* author info */}
|
||||||
<div className="bg-muted/50 rounded-lg p-6">
|
<div className="bg-muted/50 rounded-lg p-6">
|
||||||
<h2 className="text-lg font-semibold mb-4">{t("author")}</h2>
|
<h2 className="text-lg font-semibold mb-4">{t('author')}</h2>
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
<div className="relative h-8 w-8 flex-shrink-0">
|
<div className="relative h-8 w-8 flex-shrink-0">
|
||||||
{post.author?.avatar && (
|
{post.author?.avatar && (
|
||||||
@ -168,26 +169,29 @@ export default async function BlogPostPage(props: NextPageProps) {
|
|||||||
|
|
||||||
{/* categories */}
|
{/* categories */}
|
||||||
<div className="bg-muted/50 rounded-lg p-6">
|
<div className="bg-muted/50 rounded-lg p-6">
|
||||||
<h2 className="text-lg font-semibold mb-4">{t("categories")}</h2>
|
<h2 className="text-lg font-semibold mb-4">{t('categories')}</h2>
|
||||||
<ul className="flex flex-wrap gap-4">
|
<ul className="flex flex-wrap gap-4">
|
||||||
{post.categories?.filter(Boolean).map((category) => (
|
{post.categories?.filter(Boolean).map(
|
||||||
category && (
|
(category) =>
|
||||||
<li key={category.slug}>
|
category && (
|
||||||
<LocaleLink
|
<li key={category.slug}>
|
||||||
href={`/blog/category/${category.slug}`}
|
<LocaleLink
|
||||||
className="text-sm font-medium hover:text-primary"
|
href={`/blog/category/${category.slug}`}
|
||||||
>
|
className="text-sm font-medium hover:text-primary"
|
||||||
{category.name}
|
>
|
||||||
</LocaleLink>
|
{category.name}
|
||||||
</li>
|
</LocaleLink>
|
||||||
)
|
</li>
|
||||||
))}
|
)
|
||||||
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* table of contents */}
|
{/* table of contents */}
|
||||||
<div className="bg-muted/50 rounded-lg p-6 hidden lg:block">
|
<div className="bg-muted/50 rounded-lg p-6 hidden lg:block">
|
||||||
<h2 className="text-lg font-semibold mb-4">{t("tableOfContents")}</h2>
|
<h2 className="text-lg font-semibold mb-4">
|
||||||
|
{t('tableOfContents')}
|
||||||
|
</h2>
|
||||||
<div className="max-h-[calc(100vh-18rem)] overflow-y-auto">
|
<div className="max-h-[calc(100vh-18rem)] overflow-y-auto">
|
||||||
<BlogToc toc={toc} />
|
<BlogToc toc={toc} />
|
||||||
</div>
|
</div>
|
||||||
@ -200,4 +204,4 @@ export default async function BlogPostPage(props: NextPageProps) {
|
|||||||
{/* TODO: add newsletter */}
|
{/* TODO: add newsletter */}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { Footer } from "@/components/layout/footer";
|
import { Footer } from '@/components/layout/footer';
|
||||||
import { Navbar } from "@/components/layout/navbar";
|
import { Navbar } from '@/components/layout/navbar';
|
||||||
|
import { ReactNode } from 'react';
|
||||||
|
|
||||||
export default function MarketingLayout({ children }: { children: React.ReactNode }) {
|
export default function MarketingLayout({ children }: { children: ReactNode }) {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col min-h-screen">
|
<div className="flex flex-col min-h-screen">
|
||||||
<Navbar scroll={true} />
|
<Navbar scroll={true} />
|
||||||
@ -9,4 +10,4 @@ export default function MarketingLayout({ children }: { children: React.ReactNod
|
|||||||
<Footer />
|
<Footer />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import Pricing3 from "@/components/blocks/pricing/pricing-3";
|
import Pricing3 from '@/components/blocks/pricing/pricing-3';
|
||||||
import Pricing4 from "@/components/blocks/pricing/pricing-4";
|
import Pricing4 from '@/components/blocks/pricing/pricing-4';
|
||||||
import Pricing5 from "@/components/blocks/pricing/pricing-5";
|
import Pricing5 from '@/components/blocks/pricing/pricing-5';
|
||||||
import PricingComparator from "@/components/pricing-comparator";
|
import PricingComparator from '@/components/pricing-comparator';
|
||||||
import { getTranslations } from 'next-intl/server';
|
import { getTranslations } from 'next-intl/server';
|
||||||
|
|
||||||
interface PricingPageProps {
|
interface PricingPageProps {
|
||||||
params: Promise<{ locale: string }>;
|
params: Promise<{ locale: string }>;
|
||||||
};
|
}
|
||||||
|
|
||||||
export default async function PricingPage(props: PricingPageProps) {
|
export default async function PricingPage(props: PricingPageProps) {
|
||||||
const params = await props.params;
|
const params = await props.params;
|
||||||
|
@ -2,12 +2,12 @@ import { notFound } from 'next/navigation';
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Catching unknown routes
|
* Catching unknown routes
|
||||||
*
|
*
|
||||||
* all requests that are matched within the [locale] segment will render
|
* all requests that are matched within the [locale] segment will render
|
||||||
* the not-found page when an unknown route is encountered (e.g. /en/unknown).
|
* the not-found page when an unknown route is encountered (e.g. /en/unknown).
|
||||||
*
|
*
|
||||||
* https://next-intl.dev/docs/environments/error-files#catching-unknown-routes
|
* https://next-intl.dev/docs/environments/error-files#catching-unknown-routes
|
||||||
*/
|
*/
|
||||||
export default function CatchAllPage() {
|
export default function CatchAllPage() {
|
||||||
notFound();
|
notFound();
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { ErrorCard } from "@/components/auth/error-card";
|
import { ErrorCard } from '@/components/auth/error-card';
|
||||||
import { siteConfig } from "@/config/site";
|
import { siteConfig } from '@/config/site';
|
||||||
import { constructMetadata } from "@/lib/metadata";
|
import { constructMetadata } from '@/lib/metadata';
|
||||||
import { Routes } from "@/routes";
|
import { Routes } from '@/routes';
|
||||||
|
|
||||||
export const metadata = constructMetadata({
|
export const metadata = constructMetadata({
|
||||||
title: "Auth Error",
|
title: 'Auth Error',
|
||||||
description: "Auth Error",
|
description: 'Auth Error',
|
||||||
canonicalUrl: `${siteConfig.url}${Routes.AuthError}`,
|
canonicalUrl: `${siteConfig.url}${Routes.AuthError}`,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { ForgotPasswordForm } from "@/components/auth/forgot-password-form";
|
import { ForgotPasswordForm } from '@/components/auth/forgot-password-form';
|
||||||
import { siteConfig } from "@/config/site";
|
import { siteConfig } from '@/config/site';
|
||||||
import { constructMetadata } from "@/lib/metadata";
|
import { constructMetadata } from '@/lib/metadata';
|
||||||
import { Routes } from "@/routes";
|
import { Routes } from '@/routes';
|
||||||
|
|
||||||
export const metadata = constructMetadata({
|
export const metadata = constructMetadata({
|
||||||
title: "Forgot Password",
|
title: 'Forgot Password',
|
||||||
description: "Forgot your password? Reset it.",
|
description: 'Forgot your password? Reset it.',
|
||||||
canonicalUrl: `${siteConfig.url}${Routes.ForgotPassword}`,
|
canonicalUrl: `${siteConfig.url}${Routes.ForgotPassword}`,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import BackButtonSmall from "@/components/shared/back-button-small";
|
import BackButtonSmall from '@/components/shared/back-button-small';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* auth layout is different from other public layouts,
|
* auth layout is different from other public layouts,
|
||||||
@ -12,9 +12,7 @@ export default function AuthLayout({
|
|||||||
return (
|
return (
|
||||||
<div className="flex min-h-svh flex-col items-center justify-center gap-6 bg-muted p-6 md:p-10">
|
<div className="flex min-h-svh flex-col items-center justify-center gap-6 bg-muted p-6 md:p-10">
|
||||||
<BackButtonSmall className="absolute top-6 left-6" />
|
<BackButtonSmall className="absolute top-6 left-6" />
|
||||||
<div className="flex w-full max-w-sm flex-col gap-6">
|
<div className="flex w-full max-w-sm flex-col gap-6">{children}</div>
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Loader2Icon } from "lucide-react";
|
import { Loader2Icon } from 'lucide-react';
|
||||||
|
|
||||||
export default function Loading() {
|
export default function Loading() {
|
||||||
return <Loader2Icon className="my-32 mx-auto size-6 animate-spin" />;
|
return <Loader2Icon className="my-32 mx-auto size-6 animate-spin" />;
|
||||||
|
@ -1,36 +1,36 @@
|
|||||||
import { LoginForm } from "@/components/auth/login-form";
|
import { LoginForm } from '@/components/auth/login-form';
|
||||||
import { siteConfig } from "@/config/site";
|
import { siteConfig } from '@/config/site';
|
||||||
import { LocaleLink } from "@/i18n/navigation";
|
import { LocaleLink } from '@/i18n/navigation';
|
||||||
import { constructMetadata } from "@/lib/metadata";
|
import { constructMetadata } from '@/lib/metadata';
|
||||||
import { Routes } from "@/routes";
|
import { Routes } from '@/routes';
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from 'next-intl';
|
||||||
|
|
||||||
export const metadata = constructMetadata({
|
export const metadata = constructMetadata({
|
||||||
title: "Login",
|
title: 'Login',
|
||||||
description: "Login to your account",
|
description: 'Login to your account',
|
||||||
canonicalUrl: `${siteConfig.url}${Routes.Login}`,
|
canonicalUrl: `${siteConfig.url}${Routes.Login}`,
|
||||||
});
|
});
|
||||||
|
|
||||||
const LoginPage = () => {
|
const LoginPage = () => {
|
||||||
const t = useTranslations("AuthPage.login");
|
const t = useTranslations('AuthPage.login');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-4">
|
<div className="flex flex-col gap-4">
|
||||||
<LoginForm />
|
<LoginForm />
|
||||||
<div className="text-balance text-center text-xs text-muted-foreground">
|
<div className="text-balance text-center text-xs text-muted-foreground">
|
||||||
{t("byClickingContinue")}
|
{t('byClickingContinue')}
|
||||||
<LocaleLink
|
<LocaleLink
|
||||||
href={Routes.TermsOfService}
|
href={Routes.TermsOfService}
|
||||||
className="underline underline-offset-4 hover:text-primary"
|
className="underline underline-offset-4 hover:text-primary"
|
||||||
>
|
>
|
||||||
{t("termsOfService")}
|
{t('termsOfService')}
|
||||||
</LocaleLink>{" "}
|
</LocaleLink>{' '}
|
||||||
{t("and")}{" "}
|
{t('and')}{' '}
|
||||||
<LocaleLink
|
<LocaleLink
|
||||||
href={Routes.PrivacyPolicy}
|
href={Routes.PrivacyPolicy}
|
||||||
className="underline underline-offset-4 hover:text-primary"
|
className="underline underline-offset-4 hover:text-primary"
|
||||||
>
|
>
|
||||||
{t("privacyPolicy")}
|
{t('privacyPolicy')}
|
||||||
</LocaleLink>
|
</LocaleLink>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { RegisterForm } from "@/components/auth/register-form";
|
import { RegisterForm } from '@/components/auth/register-form';
|
||||||
import { siteConfig } from "@/config/site";
|
import { siteConfig } from '@/config/site';
|
||||||
import { constructMetadata } from "@/lib/metadata";
|
import { constructMetadata } from '@/lib/metadata';
|
||||||
import { Routes } from "@/routes";
|
import { Routes } from '@/routes';
|
||||||
|
|
||||||
export const metadata = constructMetadata({
|
export const metadata = constructMetadata({
|
||||||
title: "Register",
|
title: 'Register',
|
||||||
description: "Create an account to get started",
|
description: 'Create an account to get started',
|
||||||
canonicalUrl: `${siteConfig.url}${Routes.Register}`,
|
canonicalUrl: `${siteConfig.url}${Routes.Register}`,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { ResetPasswordForm } from "@/components/auth/reset-password-form";
|
import { ResetPasswordForm } from '@/components/auth/reset-password-form';
|
||||||
import { siteConfig } from "@/config/site";
|
import { siteConfig } from '@/config/site';
|
||||||
import { constructMetadata } from "@/lib/metadata";
|
import { constructMetadata } from '@/lib/metadata';
|
||||||
|
|
||||||
export const metadata = constructMetadata({
|
export const metadata = constructMetadata({
|
||||||
title: "Reset Password",
|
title: 'Reset Password',
|
||||||
description: "Set a new password",
|
description: 'Set a new password',
|
||||||
canonicalUrl: `${siteConfig.url}/auth/reset-password`,
|
canonicalUrl: `${siteConfig.url}/auth/reset-password`,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
"use client";
|
'use client';
|
||||||
|
|
||||||
import { lazy } from "react";
|
import { lazy } from 'react';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Move error content to a separate chunk and load it only when needed
|
* Move error content to a separate chunk and load it only when needed
|
||||||
*
|
*
|
||||||
* Note that error.tsx is loaded right after your app has initialized.
|
* Note that error.tsx is loaded right after your app has initialized.
|
||||||
* If your app is performance-sensitive and you want to avoid loading translation functionality
|
* If your app is performance-sensitive and you want to avoid loading translation functionality
|
||||||
* from next-intl as part of this bundle, you can export a lazy reference from your error file.
|
* from next-intl as part of this bundle, you can export a lazy reference from your error file.
|
||||||
* https://next-intl.dev/docs/environments/error-files#errorjs
|
* https://next-intl.dev/docs/environments/error-files#errorjs
|
||||||
*/
|
*/
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { fontSourceSans, fontSourceSerif4 } from "@/assets/fonts";
|
import { fontSourceSans, fontSourceSerif4 } from '@/assets/fonts';
|
||||||
import { TailwindIndicator } from '@/components/tailwind-indicator';
|
import { TailwindIndicator } from '@/components/tailwind-indicator';
|
||||||
import { routing } from '@/i18n/routing';
|
import { routing } from '@/i18n/routing';
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from '@/lib/utils';
|
||||||
import { GeistMono } from "geist/font/mono";
|
import { GeistMono } from 'geist/font/mono';
|
||||||
import { GeistSans } from "geist/font/sans";
|
import { GeistSans } from 'geist/font/sans';
|
||||||
import { NextIntlClientProvider } from 'next-intl';
|
import { NextIntlClientProvider } from 'next-intl';
|
||||||
import { getMessages } from 'next-intl/server';
|
import { getMessages } from 'next-intl/server';
|
||||||
import { notFound } from 'next/navigation';
|
import { notFound } from 'next/navigation';
|
||||||
@ -14,48 +14,54 @@ import { Providers } from './providers';
|
|||||||
import '@/styles/globals.css';
|
import '@/styles/globals.css';
|
||||||
|
|
||||||
interface LocaleLayoutProps {
|
interface LocaleLayoutProps {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
params: Promise<{ locale: string }>;
|
params: Promise<{ locale: string }>;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 1. Locale Layout
|
* 1. Locale Layout
|
||||||
* https://next-intl.dev/docs/getting-started/app-router/with-i18n-routing#layout
|
* https://next-intl.dev/docs/getting-started/app-router/with-i18n-routing#layout
|
||||||
*
|
*
|
||||||
* 2. NextIntlClientProvider
|
* 2. NextIntlClientProvider
|
||||||
* https://next-intl.dev/docs/usage/configuration#nextintlclientprovider
|
* https://next-intl.dev/docs/usage/configuration#nextintlclientprovider
|
||||||
*/
|
*/
|
||||||
export default async function LocaleLayout({ children, params }: LocaleLayoutProps) {
|
export default async function LocaleLayout({
|
||||||
const { locale } = await params;
|
children,
|
||||||
|
params,
|
||||||
|
}: LocaleLayoutProps) {
|
||||||
|
const { locale } = await params;
|
||||||
|
|
||||||
// Ensure that the incoming `locale` is valid
|
// Ensure that the incoming `locale` is valid
|
||||||
if (!routing.locales.includes(locale as any)) {
|
if (!routing.locales.includes(locale as any)) {
|
||||||
notFound();
|
notFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Providing all messages to the client side
|
// Providing all messages to the client side
|
||||||
const messages = await getMessages();
|
const messages = await getMessages();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<html lang={locale} suppressHydrationWarning>
|
<html lang={locale} suppressHydrationWarning>
|
||||||
<body suppressHydrationWarning className={cn(
|
<body
|
||||||
"size-full antialiased",
|
suppressHydrationWarning
|
||||||
GeistSans.className,
|
className={cn(
|
||||||
fontSourceSerif4.variable,
|
'size-full antialiased',
|
||||||
fontSourceSans.variable,
|
GeistSans.className,
|
||||||
GeistSans.variable,
|
fontSourceSerif4.variable,
|
||||||
GeistMono.variable,
|
fontSourceSans.variable,
|
||||||
)}>
|
GeistSans.variable,
|
||||||
<NextIntlClientProvider messages={messages}>
|
GeistMono.variable
|
||||||
<Providers>
|
)}
|
||||||
{children}
|
>
|
||||||
|
<NextIntlClientProvider messages={messages}>
|
||||||
|
<Providers>
|
||||||
|
{children}
|
||||||
|
|
||||||
<Toaster richColors position="top-right" offset={64} />
|
<Toaster richColors position="top-right" offset={64} />
|
||||||
|
|
||||||
<TailwindIndicator />
|
<TailwindIndicator />
|
||||||
</Providers>
|
</Providers>
|
||||||
</NextIntlClientProvider>
|
</NextIntlClientProvider>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Loader2Icon } from "lucide-react";
|
import { Loader2Icon } from 'lucide-react';
|
||||||
|
|
||||||
export default function Loading() {
|
export default function Loading() {
|
||||||
return <Loader2Icon className="my-32 mx-auto size-6 animate-spin" />;
|
return <Loader2Icon className="my-32 mx-auto size-6 animate-spin" />;
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { Logo } from "@/components/logo";
|
import { Logo } from '@/components/logo';
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from '@/components/ui/button';
|
||||||
import { LocaleLink } from "@/i18n/navigation";
|
import { LocaleLink } from '@/i18n/navigation';
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from 'next-intl';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Note that `app/[locale]/[...rest]/page.tsx`
|
* Note that `app/[locale]/[...rest]/page.tsx`
|
||||||
* is necessary for this page to render.
|
* is necessary for this page to render.
|
||||||
*
|
*
|
||||||
* https://next-intl.dev/docs/environments/error-files#not-foundjs
|
* https://next-intl.dev/docs/environments/error-files#not-foundjs
|
||||||
* https://next-intl.dev/docs/environments/error-files#catching-non-localized-requests
|
* https://next-intl.dev/docs/environments/error-files#catching-non-localized-requests
|
||||||
*/
|
*/
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
"use client";
|
'use client';
|
||||||
|
|
||||||
import * as React from "react";
|
import * as React from 'react';
|
||||||
import { ThemeProvider } from "next-themes";
|
import { ThemeProvider } from 'next-themes';
|
||||||
import { TooltipProvider } from "@/components/ui/tooltip";
|
import { TooltipProvider } from '@/components/ui/tooltip';
|
||||||
|
import { PropsWithChildren } from 'react';
|
||||||
|
|
||||||
export function Providers({
|
export function Providers({ children }: PropsWithChildren) {
|
||||||
children
|
|
||||||
}: React.PropsWithChildren) {
|
|
||||||
return (
|
return (
|
||||||
<ThemeProvider
|
<ThemeProvider
|
||||||
attribute="class"
|
attribute="class"
|
||||||
@ -14,9 +13,7 @@ export function Providers({
|
|||||||
enableSystem
|
enableSystem
|
||||||
disableTransitionOnChange
|
disableTransitionOnChange
|
||||||
>
|
>
|
||||||
<TooltipProvider>
|
<TooltipProvider>{children}</TooltipProvider>
|
||||||
{children}
|
|
||||||
</TooltipProvider>
|
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { auth } from "@/lib/auth"; // path to your auth file
|
import { auth } from '@/lib/auth'; // path to your auth file
|
||||||
import { toNextJsHandler } from "better-auth/next-js";
|
import { toNextJsHandler } from 'better-auth/next-js';
|
||||||
|
|
||||||
export const { POST, GET } = toNextJsHandler(auth);
|
export const { POST, GET } = toNextJsHandler(auth);
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import {ReactNode} from 'react';
|
import { ReactNode } from 'react';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Since we have a `not-found.tsx` page on the root, a layout file
|
* Since we have a `not-found.tsx` page on the root, a layout file
|
||||||
* is required, even if it's just passing children through.
|
* is required, even if it's just passing children through.
|
||||||
*
|
*
|
||||||
* https://next-intl.dev/docs/environments/error-files#catching-non-localized-requests
|
* https://next-intl.dev/docs/environments/error-files#catching-non-localized-requests
|
||||||
*/
|
*/
|
||||||
export default function RootLayout({children}: Props) {
|
export default function RootLayout({ children }: Props) {
|
||||||
return children;
|
return children;
|
||||||
}
|
}
|
||||||
|
@ -3,12 +3,12 @@ import { siteConfig } from '@/config/site';
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates the Web App Manifest for the application
|
* Generates the Web App Manifest for the application
|
||||||
*
|
*
|
||||||
* TODO: https://github.com/amannn/next-intl/blob/main/examples/example-app-router/src/app/manifest.ts
|
* TODO: https://github.com/amannn/next-intl/blob/main/examples/example-app-router/src/app/manifest.ts
|
||||||
*
|
*
|
||||||
* The manifest.json provides metadata used when the web app is installed on a
|
* The manifest.json provides metadata used when the web app is installed on a
|
||||||
* user's mobile device or desktop. See https://web.dev/add-manifest/
|
* user's mobile device or desktop. See https://web.dev/add-manifest/
|
||||||
*
|
*
|
||||||
* @returns {MetadataRoute.Manifest} The manifest configuration object
|
* @returns {MetadataRoute.Manifest} The manifest configuration object
|
||||||
*/
|
*/
|
||||||
export default function manifest(): MetadataRoute.Manifest {
|
export default function manifest(): MetadataRoute.Manifest {
|
||||||
@ -25,14 +25,14 @@ export default function manifest(): MetadataRoute.Manifest {
|
|||||||
src: '/android-chrome-192x192.png',
|
src: '/android-chrome-192x192.png',
|
||||||
sizes: '192x192',
|
sizes: '192x192',
|
||||||
type: 'image/png',
|
type: 'image/png',
|
||||||
purpose: 'maskable'
|
purpose: 'maskable',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
src: '/android-chrome-512x512.png',
|
src: '/android-chrome-512x512.png',
|
||||||
sizes: '512x512',
|
sizes: '512x512',
|
||||||
type: 'image/png',
|
type: 'image/png',
|
||||||
purpose: 'maskable'
|
purpose: 'maskable',
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
"use client";
|
'use client';
|
||||||
|
|
||||||
import Error from "next/error";
|
import Error from 'next/error';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Catching non-localized requests
|
* Catching non-localized requests
|
||||||
*
|
*
|
||||||
* This page renders when a route like `/unknown.txt` is requested.
|
* This page renders when a route like `/unknown.txt` is requested.
|
||||||
* In this case, the layout at `app/[locale]/layout.tsx` receives
|
* In this case, the layout at `app/[locale]/layout.tsx` receives
|
||||||
* an invalid value as the `[locale]` param and calls `notFound()`.
|
* an invalid value as the `[locale]` param and calls `notFound()`.
|
||||||
*
|
*
|
||||||
* https://next-intl.dev/docs/environments/error-files#catching-non-localized-requests
|
* https://next-intl.dev/docs/environments/error-files#catching-non-localized-requests
|
||||||
*/
|
*/
|
||||||
export default function GlobalNotFound() {
|
export default function GlobalNotFound() {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { siteConfig } from "@/config/site";
|
import { siteConfig } from '@/config/site';
|
||||||
import type { MetadataRoute } from "next";
|
import type { MetadataRoute } from 'next';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* https://nextjs.org/docs/app/api-reference/file-conventions/metadata/robots
|
* https://nextjs.org/docs/app/api-reference/file-conventions/metadata/robots
|
||||||
@ -7,8 +7,8 @@ import type { MetadataRoute } from "next";
|
|||||||
export default function robots(): MetadataRoute.Robots {
|
export default function robots(): MetadataRoute.Robots {
|
||||||
return {
|
return {
|
||||||
rules: {
|
rules: {
|
||||||
userAgent: "*",
|
userAgent: '*',
|
||||||
allow: "/",
|
allow: '/',
|
||||||
},
|
},
|
||||||
sitemap: `${siteConfig.url}/sitemap.xml`,
|
sitemap: `${siteConfig.url}/sitemap.xml`,
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user