mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
chore(website): make biome happy
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { notFound } from 'next/navigation'
|
||||
import { notFound } from "next/navigation";
|
||||
|
||||
export default function CatchAll() {
|
||||
notFound()
|
||||
notFound();
|
||||
}
|
||||
|
||||
@@ -1,94 +1,90 @@
|
||||
import clsx from 'clsx'
|
||||
import { Inter, Lexend } from 'next/font/google'
|
||||
import '@/styles/tailwind.css'
|
||||
import GoogleAnalytics from '@/components/analitycs/google'
|
||||
import clsx from "clsx";
|
||||
import { Inter, Lexend } from "next/font/google";
|
||||
import "@/styles/tailwind.css";
|
||||
import GoogleAnalytics from "@/components/analitycs/google";
|
||||
|
||||
import { NextIntlClientProvider } from 'next-intl'
|
||||
import { getMessages } from 'next-intl/server'
|
||||
import { NextIntlClientProvider } from "next-intl";
|
||||
import { getMessages } from "next-intl/server";
|
||||
|
||||
import type { Metadata } from 'next'
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: {
|
||||
default: 'Dokploy - Effortless Deployment Solutions',
|
||||
template: '%s | Simplify Your DevOps',
|
||||
default: "Dokploy - Effortless Deployment Solutions",
|
||||
template: "%s | Simplify Your DevOps",
|
||||
},
|
||||
alternates: {
|
||||
canonical: 'https://dokploy.com',
|
||||
canonical: "https://dokploy.com",
|
||||
languages: {
|
||||
en: 'https://dokploy.com',
|
||||
en: "https://dokploy.com",
|
||||
},
|
||||
},
|
||||
description:
|
||||
'Streamline your deployment process with Dokploy. Effortlessly manage applications and databases on any VPS using Docker and Traefik for improved performance and security.',
|
||||
applicationName: 'Dokploy',
|
||||
"Streamline your deployment process with Dokploy. Effortlessly manage applications and databases on any VPS using Docker and Traefik for improved performance and security.",
|
||||
applicationName: "Dokploy",
|
||||
keywords: [
|
||||
'Dokploy',
|
||||
'Docker',
|
||||
'Traefik',
|
||||
'deployment',
|
||||
'VPS',
|
||||
'application management',
|
||||
'database management',
|
||||
'DevOps',
|
||||
'cloud infrastructure',
|
||||
'UI Self hosted',
|
||||
"Dokploy",
|
||||
"Docker",
|
||||
"Traefik",
|
||||
"deployment",
|
||||
"VPS",
|
||||
"application management",
|
||||
"database management",
|
||||
"DevOps",
|
||||
"cloud infrastructure",
|
||||
"UI Self hosted",
|
||||
],
|
||||
referrer: 'origin',
|
||||
robots: 'index, follow',
|
||||
referrer: "origin",
|
||||
robots: "index, follow",
|
||||
openGraph: {
|
||||
type: 'website',
|
||||
url: 'https://dokploy.com',
|
||||
title: 'Dokploy - Effortless Deployment Solutions',
|
||||
type: "website",
|
||||
url: "https://dokploy.com",
|
||||
title: "Dokploy - Effortless Deployment Solutions",
|
||||
description:
|
||||
'Simplify your DevOps with Dokploy. Deploy applications and manage databases efficiently on any VPS.',
|
||||
siteName: 'Dokploy',
|
||||
"Simplify your DevOps with Dokploy. Deploy applications and manage databases efficiently on any VPS.",
|
||||
siteName: "Dokploy",
|
||||
images: [
|
||||
{
|
||||
url: 'http://dokploy.com/og.png',
|
||||
url: "http://dokploy.com/og.png",
|
||||
},
|
||||
],
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
site: '@Dokploy',
|
||||
creator: '@Dokploy',
|
||||
title: 'Dokploy - Simplify Your DevOps',
|
||||
card: "summary_large_image",
|
||||
site: "@Dokploy",
|
||||
creator: "@Dokploy",
|
||||
title: "Dokploy - Simplify Your DevOps",
|
||||
description:
|
||||
'Deploy applications and manage databases with ease using Dokploy. Learn how our platform can elevate your infrastructure management.',
|
||||
images: 'https://dokploy.com/og.png',
|
||||
"Deploy applications and manage databases with ease using Dokploy. Learn how our platform can elevate your infrastructure management.",
|
||||
images: "https://dokploy.com/og.png",
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
const inter = Inter({
|
||||
subsets: ['latin'],
|
||||
display: 'swap',
|
||||
variable: '--font-inter',
|
||||
})
|
||||
subsets: ["latin"],
|
||||
display: "swap",
|
||||
variable: "--font-inter",
|
||||
});
|
||||
|
||||
const lexend = Lexend({
|
||||
subsets: ['latin'],
|
||||
display: 'swap',
|
||||
variable: '--font-lexend',
|
||||
})
|
||||
subsets: ["latin"],
|
||||
display: "swap",
|
||||
variable: "--font-lexend",
|
||||
});
|
||||
|
||||
export default async function RootLayout({
|
||||
children,
|
||||
params,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
params: { locale: string }
|
||||
children: React.ReactNode;
|
||||
params: { locale: string };
|
||||
}) {
|
||||
const { locale } = params
|
||||
const messages = await getMessages()
|
||||
const { locale } = params;
|
||||
const messages = await getMessages();
|
||||
return (
|
||||
<html
|
||||
lang={locale}
|
||||
className={clsx(
|
||||
'h-full scroll-smooth',
|
||||
inter.variable,
|
||||
lexend.variable,
|
||||
)}
|
||||
className={clsx("h-full scroll-smooth", inter.variable, lexend.variable)}
|
||||
>
|
||||
<GoogleAnalytics />
|
||||
<body className="flex h-full flex-col">
|
||||
@@ -97,5 +93,5 @@ export default async function RootLayout({
|
||||
</NextIntlClientProvider>
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { SlimLayout } from '@/components/SlimLayout'
|
||||
import { SlimLayout } from "@/components/SlimLayout";
|
||||
|
||||
export default function NotFound() {
|
||||
return <SlimLayout />
|
||||
return <SlimLayout />;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { CallToAction } from '@/components/CallToAction'
|
||||
import { Faqs } from '@/components/Faqs'
|
||||
import { Footer } from '@/components/Footer'
|
||||
import { Header } from '@/components/Header'
|
||||
import { Hero } from '@/components/Hero'
|
||||
import { PrimaryFeatures } from '@/components/PrimaryFeatures'
|
||||
import { SecondaryFeatures } from '@/components/SecondaryFeatures'
|
||||
import { Testimonials } from '../../components/Testimonials'
|
||||
import { CallToAction } from "@/components/CallToAction";
|
||||
import { Faqs } from "@/components/Faqs";
|
||||
import { Footer } from "@/components/Footer";
|
||||
import { Header } from "@/components/Header";
|
||||
import { Hero } from "@/components/Hero";
|
||||
import { PrimaryFeatures } from "@/components/PrimaryFeatures";
|
||||
import { SecondaryFeatures } from "@/components/SecondaryFeatures";
|
||||
import { Testimonials } from "../../components/Testimonials";
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
@@ -21,5 +21,5 @@ export default function Home() {
|
||||
<Footer />
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { ReactNode } from 'react'
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
type Props = {
|
||||
children: ReactNode
|
||||
}
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
// Since we have a `not-found.tsx` page on the root, a layout file
|
||||
// is required, even if it's just passing children through.
|
||||
export default function RootLayout({ children }: Props) {
|
||||
return children
|
||||
return children;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
'use client'
|
||||
"use client";
|
||||
|
||||
import Error from 'next/error'
|
||||
import NextError from "next/error";
|
||||
|
||||
export default function NotFound() {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body>
|
||||
<Error statusCode={404} />
|
||||
<NextError statusCode={404} />
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,58 +1,57 @@
|
||||
import { useTranslations } from 'next-intl'
|
||||
import { Container } from './Container'
|
||||
import { useTranslations } from "next-intl";
|
||||
import { Container } from "./Container";
|
||||
|
||||
const faqs = [
|
||||
[
|
||||
{
|
||||
question: 'faq.q1',
|
||||
answer: 'faq.a1',
|
||||
question: "faq.q1",
|
||||
answer: "faq.a1",
|
||||
},
|
||||
{
|
||||
question: 'faq.q2',
|
||||
answer: 'faq.a2',
|
||||
question: "faq.q2",
|
||||
answer: "faq.a2",
|
||||
},
|
||||
{
|
||||
question: 'faq.q3',
|
||||
answer: 'faq.a3',
|
||||
question: "faq.q3",
|
||||
answer: "faq.a3",
|
||||
},
|
||||
{
|
||||
question: 'faq.q4',
|
||||
answer: 'faq.a4',
|
||||
question: "faq.q4",
|
||||
answer: "faq.a4",
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
question: 'faq.q5',
|
||||
answer: 'faq.a5',
|
||||
question: "faq.q5",
|
||||
answer: "faq.a5",
|
||||
},
|
||||
{
|
||||
question: 'faq.q6',
|
||||
answer: 'faq.a6',
|
||||
question: "faq.q6",
|
||||
answer: "faq.a6",
|
||||
},
|
||||
{
|
||||
question: 'faq.q7',
|
||||
question: "faq.q7",
|
||||
answer: "faq.a7",
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
question:
|
||||
'faq.q8',
|
||||
answer: 'faq.a8',
|
||||
question: "faq.q8",
|
||||
answer: "faq.a8",
|
||||
},
|
||||
{
|
||||
question: 'faq.q9',
|
||||
answer: 'faq.a9',
|
||||
question: "faq.q9",
|
||||
answer: "faq.a9",
|
||||
},
|
||||
{
|
||||
question: 'faq.q10',
|
||||
answer: 'faq.a10',
|
||||
question: "faq.q10",
|
||||
answer: "faq.a10",
|
||||
},
|
||||
],
|
||||
]
|
||||
];
|
||||
|
||||
export function Faqs() {
|
||||
const t = useTranslations('HomePage')
|
||||
const t = useTranslations("HomePage");
|
||||
return (
|
||||
<section
|
||||
id="faqs"
|
||||
@@ -65,10 +64,10 @@ export function Faqs() {
|
||||
id="faq-title"
|
||||
className="font-display text-3xl tracking-tight text-primary sm:text-4xl"
|
||||
>
|
||||
{t('faq.title')}
|
||||
{t("faq.title")}
|
||||
</h2>
|
||||
<p className="mt-4 text-lg tracking-tight text-muted-foreground">
|
||||
{t('faq.des')}
|
||||
{t("faq.des")}
|
||||
</p>
|
||||
</div>
|
||||
<ul className="mx-auto mt-16 grid max-w-2xl grid-cols-1 gap-8 lg:max-w-none lg:grid-cols-3">
|
||||
@@ -91,5 +90,5 @@ export function Faqs() {
|
||||
</ul>
|
||||
</Container>
|
||||
</section>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import Link from 'next/link'
|
||||
import Link from "next/link";
|
||||
|
||||
import { Container } from './Container'
|
||||
import { NavLink } from './NavLink'
|
||||
import { Logo } from './shared/Logo'
|
||||
import { useTranslations } from 'next-intl'
|
||||
import { useTranslations } from "next-intl";
|
||||
import { Container } from "./Container";
|
||||
import { NavLink } from "./NavLink";
|
||||
import { Logo } from "./shared/Logo";
|
||||
|
||||
export function Footer() {
|
||||
const t = useTranslations('HomePage')
|
||||
const linkT = useTranslations('Link')
|
||||
const t = useTranslations("HomePage");
|
||||
const linkT = useTranslations("Link");
|
||||
|
||||
return (
|
||||
<footer className="bg-black">
|
||||
@@ -22,14 +22,10 @@ export function Footer() {
|
||||
|
||||
<nav className="mt-10 text-sm" aria-label="quick links">
|
||||
<div className="-my-1 flex flex-wrap justify-center gap-6">
|
||||
<NavLink href="/#features">
|
||||
{t('navigation.features')}
|
||||
</NavLink>
|
||||
<NavLink href="/#faqs">
|
||||
{t('navigation.faqs')}
|
||||
</NavLink>
|
||||
<NavLink href={linkT('docs.intro')} target="_blank">
|
||||
{t('navigation.docs')}
|
||||
<NavLink href="/#features">{t("navigation.features")}</NavLink>
|
||||
<NavLink href="/#faqs">{t("navigation.faqs")}</NavLink>
|
||||
<NavLink href={linkT("docs.intro")} target="_blank">
|
||||
{t("navigation.docs")}
|
||||
</NavLink>
|
||||
</div>
|
||||
</nav>
|
||||
@@ -62,12 +58,12 @@ export function Footer() {
|
||||
</Link>
|
||||
</div>
|
||||
<p className="mt-6 text-sm text-muted-foreground sm:mt-0">
|
||||
{t('footer.copyright', {
|
||||
{t("footer.copyright", {
|
||||
year: new Date().getFullYear(),
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
</Container>
|
||||
</footer>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,40 +1,40 @@
|
||||
'use client'
|
||||
"use client";
|
||||
|
||||
import { cn } from '@/lib/utils'
|
||||
import { Popover, Transition } from '@headlessui/react'
|
||||
import { HeartIcon } from 'lucide-react'
|
||||
import { Fragment, JSX, SVGProps } from 'react'
|
||||
import { Container } from './Container'
|
||||
import { NavLink } from './NavLink'
|
||||
import { trackGAEvent } from './analitycs'
|
||||
import { Logo } from './shared/Logo'
|
||||
import { Button, buttonVariants } from './ui/button'
|
||||
import { useLocale, useTranslations } from 'next-intl'
|
||||
import { Link, useRouter } from '@/i18n/routing'
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
} from '@/components/ui/select'
|
||||
} from "@/components/ui/select";
|
||||
import { Link, useRouter } from "@/i18n/routing";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Popover, Transition } from "@headlessui/react";
|
||||
import { HeartIcon } from "lucide-react";
|
||||
import { useLocale, useTranslations } from "next-intl";
|
||||
import { Fragment, type JSX, type SVGProps } from "react";
|
||||
import { Container } from "./Container";
|
||||
import { NavLink } from "./NavLink";
|
||||
import { trackGAEvent } from "./analitycs";
|
||||
import { Logo } from "./shared/Logo";
|
||||
import { Button, buttonVariants } from "./ui/button";
|
||||
|
||||
function MobileNavLink({
|
||||
href,
|
||||
children,
|
||||
target,
|
||||
}: {
|
||||
href: string
|
||||
children: React.ReactNode
|
||||
target?: string
|
||||
href: string;
|
||||
children: React.ReactNode;
|
||||
target?: string;
|
||||
}) {
|
||||
return (
|
||||
<Popover.Button
|
||||
onClick={() => {
|
||||
trackGAEvent({
|
||||
action: 'Nav Link Clicked',
|
||||
category: 'Navigation',
|
||||
action: "Nav Link Clicked",
|
||||
category: "Navigation",
|
||||
label: href,
|
||||
})
|
||||
});
|
||||
}}
|
||||
as={Link}
|
||||
href={href}
|
||||
@@ -43,7 +43,7 @@ function MobileNavLink({
|
||||
>
|
||||
{children}
|
||||
</Popover.Button>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function MobileNavIcon({ open }: { open: boolean }) {
|
||||
@@ -57,20 +57,17 @@ function MobileNavIcon({ open }: { open: boolean }) {
|
||||
>
|
||||
<path
|
||||
d="M0 1H14M0 7H14M0 13H14"
|
||||
className={cn(
|
||||
'origin-center transition',
|
||||
open && 'scale-90 opacity-0',
|
||||
)}
|
||||
className={cn("origin-center transition", open && "scale-90 opacity-0")}
|
||||
/>
|
||||
<path
|
||||
d="M2 2L12 12M12 2L2 12"
|
||||
className={cn(
|
||||
'origin-center transition',
|
||||
!open && 'scale-90 opacity-0',
|
||||
"origin-center transition",
|
||||
!open && "scale-90 opacity-0",
|
||||
)}
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const I18nIcon = (props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) => (
|
||||
@@ -89,11 +86,11 @@ const I18nIcon = (props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) => (
|
||||
d="m478.33 433.6-90-218a22 22 0 0 0-40.67 0l-90 218a22 22 0 1 0 40.67 16.79L316.66 406h102.67l18.33 44.39A22 22 0 0 0 458 464a22 22 0 0 0 20.32-30.4zM334.83 362 368 281.65 401.17 362zm-66.99-19.08a22 22 0 0 0-4.89-30.7c-.2-.15-15-11.13-36.49-34.73 39.65-53.68 62.11-114.75 71.27-143.49H330a22 22 0 0 0 0-44H214V70a22 22 0 0 0-44 0v20H54a22 22 0 0 0 0 44h197.25c-9.52 26.95-27.05 69.5-53.79 108.36-31.41-41.68-43.08-68.65-43.17-68.87a22 22 0 0 0-40.58 17c.58 1.38 14.55 34.23 52.86 83.93.92 1.19 1.83 2.35 2.74 3.51-39.24 44.35-77.74 71.86-93.85 80.74a22 22 0 1 0 21.07 38.63c2.16-1.18 48.6-26.89 101.63-85.59 22.52 24.08 38 35.44 38.93 36.1a22 22 0 0 0 30.75-4.9z"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
);
|
||||
|
||||
function MobileNavigation() {
|
||||
const t = useTranslations('HomePage')
|
||||
const linkT = useTranslations('Link')
|
||||
const t = useTranslations("HomePage");
|
||||
const linkT = useTranslations("Link");
|
||||
return (
|
||||
<Popover>
|
||||
<Popover.Button
|
||||
@@ -129,30 +126,25 @@ function MobileNavigation() {
|
||||
className="absolute inset-x-0 top-full mt-4 flex origin-top flex-col rounded-2xl border border-border bg-background p-4 text-lg tracking-tight text-primary shadow-xl ring-1 ring-border/5"
|
||||
>
|
||||
<MobileNavLink href="/#features">
|
||||
{t('navigation.features')}
|
||||
{t("navigation.features")}
|
||||
</MobileNavLink>
|
||||
{/* <MobileNavLink href="/#testimonials">Testimonials</MobileNavLink> */}
|
||||
<MobileNavLink href="/#faqs">
|
||||
{t('navigation.faqs')}
|
||||
</MobileNavLink>
|
||||
<MobileNavLink
|
||||
href={linkT('docs.intro')}
|
||||
target="_blank"
|
||||
>
|
||||
{t('navigation.docs')}
|
||||
<MobileNavLink href="/#faqs">{t("navigation.faqs")}</MobileNavLink>
|
||||
<MobileNavLink href={linkT("docs.intro")} target="_blank">
|
||||
{t("navigation.docs")}
|
||||
</MobileNavLink>
|
||||
</Popover.Panel>
|
||||
</Transition.Child>
|
||||
</Transition.Root>
|
||||
</Popover>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function Header() {
|
||||
const router = useRouter()
|
||||
const locale = useLocale()
|
||||
const t = useTranslations('HomePage')
|
||||
const linkT = useTranslations('Link')
|
||||
const router = useRouter();
|
||||
const locale = useLocale();
|
||||
const t = useTranslations("HomePage");
|
||||
const linkT = useTranslations("Link");
|
||||
|
||||
return (
|
||||
<header className="bg-background py-10">
|
||||
@@ -163,57 +155,50 @@ export function Header() {
|
||||
<Logo className="h-10 w-auto" />
|
||||
</Link>
|
||||
<div className="hidden md:flex md:gap-x-6">
|
||||
<NavLink href="/#features">
|
||||
{t('navigation.features')}
|
||||
</NavLink>
|
||||
<NavLink href="/#features">{t("navigation.features")}</NavLink>
|
||||
{/* <NavLink href="/#testimonials">Testimonials</NavLink> */}
|
||||
<NavLink href="/#faqs">
|
||||
{t('navigation.faqs')}
|
||||
</NavLink>
|
||||
<NavLink href={linkT('docs.intro')} target="_blank">
|
||||
{t('navigation.docs')}
|
||||
<NavLink href="/#faqs">{t("navigation.faqs")}</NavLink>
|
||||
<NavLink href={linkT("docs.intro")} target="_blank">
|
||||
{t("navigation.docs")}
|
||||
</NavLink>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-x-2 md:gap-x-5">
|
||||
<Select
|
||||
onValueChange={(locale) => {
|
||||
router.replace('/', {
|
||||
locale: locale as 'en' | 'zh-Hans',
|
||||
})
|
||||
router.replace("/", {
|
||||
locale: locale as "en" | "zh-Hans",
|
||||
});
|
||||
}}
|
||||
value={locale}
|
||||
>
|
||||
<SelectTrigger
|
||||
className={buttonVariants({
|
||||
variant: 'outline',
|
||||
variant: "outline",
|
||||
className:
|
||||
' flex items-center gap-2 !rounded-full visited:outline-none focus-within:outline-none focus:outline-none',
|
||||
" flex items-center gap-2 !rounded-full visited:outline-none focus-within:outline-none focus:outline-none",
|
||||
})}
|
||||
>
|
||||
<I18nIcon width={20} height={20} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="en">
|
||||
{t('navigation.i18nEn')}
|
||||
</SelectItem>
|
||||
<SelectItem value="en">{t("navigation.i18nEn")}</SelectItem>
|
||||
<SelectItem value="zh-Hans">
|
||||
{t('navigation.i18nZh-Hans')}
|
||||
{t("navigation.i18nZh-Hans")}
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
<Link
|
||||
className={buttonVariants({
|
||||
variant: 'outline',
|
||||
className:
|
||||
' flex items-center gap-2 !rounded-full',
|
||||
variant: "outline",
|
||||
className: " flex items-center gap-2 !rounded-full",
|
||||
})}
|
||||
href="https://opencollective.com/dokploy"
|
||||
target="_blank"
|
||||
>
|
||||
<span className="text-sm font-semibold">
|
||||
{t('navigation.support')}{' '}
|
||||
{t("navigation.support")}{" "}
|
||||
</span>
|
||||
<HeartIcon className="animate-heartbeat size-4 fill-red-600 text-red-500 " />
|
||||
</Link>
|
||||
@@ -236,7 +221,7 @@ export function Header() {
|
||||
>
|
||||
<path d="M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189Z" />
|
||||
</svg>
|
||||
{t('navigation.discord')}
|
||||
{t("navigation.discord")}
|
||||
</Link>
|
||||
</Button>
|
||||
<div className="-mr-1 md:hidden">
|
||||
@@ -246,5 +231,5 @@ export function Header() {
|
||||
</nav>
|
||||
</Container>
|
||||
</header>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { Link } from "@/i18n/routing";
|
||||
import { trackGAEvent } from "./analitycs";
|
||||
import { Link } from '@/i18n/routing'
|
||||
|
||||
export function NavLink({
|
||||
href,
|
||||
|
||||
@@ -1,73 +1,73 @@
|
||||
'use client'
|
||||
"use client";
|
||||
|
||||
import { Tab } from '@headlessui/react'
|
||||
import { AnimatePresence, motion } from 'framer-motion'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Tab } from "@headlessui/react";
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
import { cn } from '@/lib/utils'
|
||||
import { Container } from './Container'
|
||||
import { useTranslations } from 'next-intl'
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { Container } from "./Container";
|
||||
|
||||
const features = [
|
||||
{
|
||||
title: 'primaryFeatures.projects',
|
||||
description: 'primaryFeatures.projectsDes',
|
||||
image: '/primary/projects.png',
|
||||
title: "primaryFeatures.projects",
|
||||
description: "primaryFeatures.projectsDes",
|
||||
image: "/primary/projects.png",
|
||||
},
|
||||
{
|
||||
title: 'primaryFeatures.applications',
|
||||
description: 'primaryFeatures.applicationsDes',
|
||||
image: '/primary/applications.png',
|
||||
title: "primaryFeatures.applications",
|
||||
description: "primaryFeatures.applicationsDes",
|
||||
image: "/primary/applications.png",
|
||||
},
|
||||
{
|
||||
title: 'primaryFeatures.compose',
|
||||
description: 'primaryFeatures.composeDes',
|
||||
image: '/primary/compose.png',
|
||||
title: "primaryFeatures.compose",
|
||||
description: "primaryFeatures.composeDes",
|
||||
image: "/primary/compose.png",
|
||||
},
|
||||
{
|
||||
title: 'primaryFeatures.multinode',
|
||||
description: 'primaryFeatures.multinodeDes',
|
||||
image: '/primary/multinode.png',
|
||||
title: "primaryFeatures.multinode",
|
||||
description: "primaryFeatures.multinodeDes",
|
||||
image: "/primary/multinode.png",
|
||||
},
|
||||
{
|
||||
title: 'primaryFeatures.monitoring',
|
||||
description: 'primaryFeatures.monitoringDes',
|
||||
image: '/primary/monitoring.png',
|
||||
title: "primaryFeatures.monitoring",
|
||||
description: "primaryFeatures.monitoringDes",
|
||||
image: "/primary/monitoring.png",
|
||||
},
|
||||
{
|
||||
title: 'primaryFeatures.backups',
|
||||
description: 'primaryFeatures.backupsDes',
|
||||
image: '/primary/backups.png',
|
||||
title: "primaryFeatures.backups",
|
||||
description: "primaryFeatures.backupsDes",
|
||||
image: "/primary/backups.png",
|
||||
},
|
||||
]
|
||||
];
|
||||
|
||||
export function PrimaryFeatures() {
|
||||
const t = useTranslations('HomePage')
|
||||
const t = useTranslations("HomePage");
|
||||
const [tabOrientation, setTabOrientation] = useState<
|
||||
'horizontal' | 'vertical'
|
||||
>('horizontal')
|
||||
"horizontal" | "vertical"
|
||||
>("horizontal");
|
||||
|
||||
useEffect(() => {
|
||||
const lgMediaQuery = window.matchMedia('(min-width: 1024px)')
|
||||
const lgMediaQuery = window.matchMedia("(min-width: 1024px)");
|
||||
|
||||
function onMediaQueryChange({ matches }: { matches: boolean }) {
|
||||
setTabOrientation(matches ? 'vertical' : 'horizontal')
|
||||
setTabOrientation(matches ? "vertical" : "horizontal");
|
||||
}
|
||||
|
||||
onMediaQueryChange(lgMediaQuery)
|
||||
lgMediaQuery.addEventListener('change', onMediaQueryChange)
|
||||
onMediaQueryChange(lgMediaQuery);
|
||||
lgMediaQuery.addEventListener("change", onMediaQueryChange);
|
||||
|
||||
return () => {
|
||||
lgMediaQuery.removeEventListener('change', onMediaQueryChange)
|
||||
}
|
||||
}, [])
|
||||
lgMediaQuery.removeEventListener("change", onMediaQueryChange);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const [isMounted, setIsMounted] = useState(false)
|
||||
const [isMounted, setIsMounted] = useState(false);
|
||||
|
||||
// Cambiar isMounted a true después del primer render
|
||||
useEffect(() => {
|
||||
setIsMounted(true)
|
||||
}, [])
|
||||
setIsMounted(true);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<section
|
||||
@@ -88,16 +88,16 @@ export function PrimaryFeatures() {
|
||||
<Container className="relative">
|
||||
<div className="max-w-2xl md:mx-auto md:text-center xl:max-w-none">
|
||||
<h2 className="font-display text-3xl tracking-tight text-white sm:text-4xl md:text-5xl">
|
||||
{t('primaryFeatures.title')}
|
||||
{t("primaryFeatures.title")}
|
||||
</h2>
|
||||
<p className="mt-6 text-lg tracking-tight text-muted-foreground">
|
||||
{t('primaryFeatures.des')}
|
||||
{t("primaryFeatures.des")}
|
||||
</p>
|
||||
</div>
|
||||
<Tab.Group
|
||||
as="div"
|
||||
className="mt-16 grid grid-cols-1 items-center gap-y-2 pt-10 sm:gap-y-6 md:mt-20 lg:grid-cols-12 lg:pt-0"
|
||||
vertical={tabOrientation === 'vertical'}
|
||||
vertical={tabOrientation === "vertical"}
|
||||
>
|
||||
{({ selectedIndex }) => (
|
||||
<>
|
||||
@@ -113,12 +113,11 @@ export function PrimaryFeatures() {
|
||||
initial={false}
|
||||
key={`feature-${featureIndex}`}
|
||||
className={cn(
|
||||
'group relative rounded-full px-4 py-1 transition-colors lg:rounded-l-xl lg:rounded-r-none lg:p-6 ',
|
||||
"group relative rounded-full px-4 py-1 transition-colors lg:rounded-l-xl lg:rounded-r-none lg:p-6 ",
|
||||
)}
|
||||
>
|
||||
<AnimatePresence>
|
||||
{selectedIndex ===
|
||||
featureIndex && (
|
||||
{selectedIndex === featureIndex && (
|
||||
<motion.span
|
||||
layoutId="tab"
|
||||
className="absolute inset-0 z-10 rounded-full bg-white/5 mix-blend-difference lg:rounded-l-xl lg:rounded-r-none"
|
||||
@@ -126,7 +125,7 @@ export function PrimaryFeatures() {
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
transition={{
|
||||
type: 'spring',
|
||||
type: "spring",
|
||||
bounce: 0.2,
|
||||
duration: 0.5,
|
||||
}}
|
||||
@@ -136,7 +135,7 @@ export function PrimaryFeatures() {
|
||||
<h3>
|
||||
<Tab
|
||||
className={cn(
|
||||
'font-display text-lg text-primary ui-not-focus-visible:outline-none',
|
||||
"font-display text-lg text-primary ui-not-focus-visible:outline-none",
|
||||
)}
|
||||
>
|
||||
<span className="absolute inset-0 rounded-full lg:rounded-l-xl lg:rounded-r-none" />
|
||||
@@ -145,7 +144,7 @@ export function PrimaryFeatures() {
|
||||
</h3>
|
||||
<p
|
||||
className={cn(
|
||||
'mt-2 hidden text-sm text-muted-foreground lg:block',
|
||||
"mt-2 hidden text-sm text-muted-foreground lg:block",
|
||||
)}
|
||||
>
|
||||
{t(feature.description)}
|
||||
@@ -166,19 +165,11 @@ export function PrimaryFeatures() {
|
||||
|
||||
<motion.div
|
||||
key={feature.title}
|
||||
initial={
|
||||
isMounted
|
||||
? { opacity: 0.8, x: 50 }
|
||||
: {}
|
||||
}
|
||||
animate={
|
||||
isMounted
|
||||
? { opacity: 1, x: 0 }
|
||||
: {}
|
||||
}
|
||||
initial={isMounted ? { opacity: 0.8, x: 50 } : {}}
|
||||
animate={isMounted ? { opacity: 1, x: 0 } : {}}
|
||||
exit={{ opacity: 0, x: -50 }}
|
||||
transition={{
|
||||
type: 'spring',
|
||||
type: "spring",
|
||||
bounce: 0.2,
|
||||
duration: 0.6,
|
||||
}}
|
||||
@@ -199,5 +190,5 @@ export function PrimaryFeatures() {
|
||||
</Tab.Group>
|
||||
</Container>
|
||||
</section>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,38 +1,38 @@
|
||||
'use client'
|
||||
"use client";
|
||||
|
||||
import { cn } from '@/lib/utils'
|
||||
import { Tab } from '@headlessui/react'
|
||||
import { motion } from 'framer-motion'
|
||||
import { Layers, Terminal, Users } from 'lucide-react'
|
||||
import { Container } from './Container'
|
||||
import { useTranslations } from 'next-intl'
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Tab } from "@headlessui/react";
|
||||
import { motion } from "framer-motion";
|
||||
import { Layers, Terminal, Users } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { Container } from "./Container";
|
||||
interface Feature {
|
||||
name: React.ReactNode
|
||||
summary: string
|
||||
description: string
|
||||
image: string
|
||||
icon: React.ComponentType
|
||||
name: React.ReactNode;
|
||||
summary: string;
|
||||
description: string;
|
||||
image: string;
|
||||
icon: React.ComponentType;
|
||||
}
|
||||
|
||||
const features: Array<Feature> = [
|
||||
{
|
||||
name: 'secondaryFeatures.templates',
|
||||
summary: 'secondaryFeatures.templatesSummary',
|
||||
description: 'secondaryFeatures.templatesDes',
|
||||
image: '/secondary/templates.png',
|
||||
name: "secondaryFeatures.templates",
|
||||
summary: "secondaryFeatures.templatesSummary",
|
||||
description: "secondaryFeatures.templatesDes",
|
||||
image: "/secondary/templates.png",
|
||||
icon: function ReportingIcon() {
|
||||
return (
|
||||
<>
|
||||
<Layers className="size-5 text-primary" />
|
||||
</>
|
||||
)
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'secondaryFeatures.traefik',
|
||||
summary: 'secondaryFeatures.traefikSummary',
|
||||
description: 'secondaryFeatures.traefikDes',
|
||||
image: '/secondary/traefik.png',
|
||||
name: "secondaryFeatures.traefik",
|
||||
summary: "secondaryFeatures.traefikSummary",
|
||||
description: "secondaryFeatures.traefikDes",
|
||||
image: "/secondary/traefik.png",
|
||||
icon: function ReportingIcon() {
|
||||
return (
|
||||
<>
|
||||
@@ -82,12 +82,7 @@ const features: Array<Feature> = [
|
||||
d="M299.847 285.567c10.027 58.288 105.304 42.877 91.619-15.91-12.271-52.716-94.951-38.124-91.619 15.91m-113.855 9.427c12.996 50.745 94.24 37.753 91.178-13.149-3.669-60.964-103.603-49.2-91.178 13.149m132.351 58.517c.044 7.79 1.843 15.403.289 24.148-1.935 3.656-5.729 4.043-9.001 5.52-4.524-.71-8.328-3.68-10.143-7.912-1.161-9.202.433-18.111.726-27.316l18.129 5.56z"
|
||||
fill="#fff"
|
||||
/>
|
||||
<ellipse
|
||||
cx="208.4"
|
||||
cy="286.718"
|
||||
rx="13.719"
|
||||
ry="14.86"
|
||||
/>
|
||||
<ellipse cx="208.4" cy="286.718" rx="13.719" ry="14.86" />
|
||||
<ellipse
|
||||
cx="214.64"
|
||||
cy="290.071"
|
||||
@@ -95,19 +90,9 @@ const features: Array<Feature> = [
|
||||
ry="3.777"
|
||||
fill="#fff"
|
||||
/>
|
||||
<ellipse
|
||||
cx="323.348"
|
||||
cy="283.017"
|
||||
rx="13.491"
|
||||
ry="14.86"
|
||||
/>
|
||||
<ellipse cx="323.348" cy="283.017" rx="13.491" ry="14.86" />
|
||||
<g fill="#fff">
|
||||
<ellipse
|
||||
cx="329.485"
|
||||
cy="286.371"
|
||||
rx="3.181"
|
||||
ry="3.777"
|
||||
/>
|
||||
<ellipse cx="329.485" cy="286.371" rx="3.181" ry="3.777" />
|
||||
<path d="M279.137 354.685c-5.986 14.507 3.338 43.515 19.579 22.119-1.161-9.202.433-18.111.726-27.316l-20.305 5.197z" />
|
||||
</g>
|
||||
<path
|
||||
@@ -226,60 +211,60 @@ const features: Array<Feature> = [
|
||||
</g>
|
||||
</svg>
|
||||
</>
|
||||
)
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'secondaryFeatures.users',
|
||||
summary: 'secondaryFeatures.usersSummary',
|
||||
description: 'secondaryFeatures.usersDes',
|
||||
image: '/secondary/users.png',
|
||||
name: "secondaryFeatures.users",
|
||||
summary: "secondaryFeatures.usersSummary",
|
||||
description: "secondaryFeatures.usersDes",
|
||||
image: "/secondary/users.png",
|
||||
icon: function InventoryIcon() {
|
||||
return (
|
||||
<>
|
||||
<Users className="size-5 text-primary" />
|
||||
</>
|
||||
)
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'secondaryFeatures.terminal',
|
||||
summary: 'secondaryFeatures.terminalSummary',
|
||||
description: 'secondaryFeatures.terminalDes',
|
||||
image: '/secondary/terminal.png',
|
||||
name: "secondaryFeatures.terminal",
|
||||
summary: "secondaryFeatures.terminalSummary",
|
||||
description: "secondaryFeatures.terminalDes",
|
||||
image: "/secondary/terminal.png",
|
||||
icon: function ContactsIcon() {
|
||||
return (
|
||||
<>
|
||||
<Terminal className="size-5 text-primary" />
|
||||
</>
|
||||
)
|
||||
);
|
||||
},
|
||||
},
|
||||
]
|
||||
];
|
||||
|
||||
function Feature({
|
||||
feature,
|
||||
isActive,
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentPropsWithoutRef<'div'> & {
|
||||
feature: Feature
|
||||
isActive: boolean
|
||||
}: React.ComponentPropsWithoutRef<"div"> & {
|
||||
feature: Feature;
|
||||
isActive: boolean;
|
||||
}) {
|
||||
const t = useTranslations('HomePage')
|
||||
const t = useTranslations("HomePage");
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
className,
|
||||
!isActive ? 'opacity-75 hover:opacity-100 ' : 'rounded-xl',
|
||||
' relative p-4',
|
||||
!isActive ? "opacity-75 hover:opacity-100 " : "rounded-xl",
|
||||
" relative p-4",
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
'flex size-9 items-center justify-center rounded-lg',
|
||||
isActive ? 'bg-border' : 'bg-muted',
|
||||
"flex size-9 items-center justify-center rounded-lg",
|
||||
isActive ? "bg-border" : "bg-muted",
|
||||
)}
|
||||
>
|
||||
<feature.icon />
|
||||
@@ -289,7 +274,7 @@ function Feature({
|
||||
layoutId="bubble"
|
||||
className="absolute inset-0 z-10 rounded-xl bg-white/5 mix-blend-difference"
|
||||
transition={{
|
||||
type: 'spring',
|
||||
type: "spring",
|
||||
bounce: 0.2,
|
||||
duration: 0.6,
|
||||
}}
|
||||
@@ -297,8 +282,8 @@ function Feature({
|
||||
)}
|
||||
<h3
|
||||
className={cn(
|
||||
'mt-6 text-sm font-medium',
|
||||
isActive ? 'text-primary' : 'text-primary/85',
|
||||
"mt-6 text-sm font-medium",
|
||||
isActive ? "text-primary" : "text-primary/85",
|
||||
)}
|
||||
>
|
||||
{feature.name}
|
||||
@@ -310,7 +295,7 @@ function Feature({
|
||||
{t(feature.description)}
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function FeaturesMobile() {
|
||||
@@ -318,11 +303,7 @@ function FeaturesMobile() {
|
||||
<div className="-mx-4 mt-20 flex flex-col gap-y-10 overflow-hidden px-4 sm:-mx-6 sm:px-6 lg:hidden">
|
||||
{features.map((feature) => (
|
||||
<div key={feature.summary}>
|
||||
<Feature
|
||||
feature={feature}
|
||||
className="mx-auto max-w-2xl"
|
||||
isActive
|
||||
/>
|
||||
<Feature feature={feature} className="mx-auto max-w-2xl" isActive />
|
||||
<div className="relative mt-10 pb-10">
|
||||
<div className="absolute -inset-x-4 bottom-0 top-8 bg-muted sm:-inset-x-6" />
|
||||
<div className="relative mx-auto w-[52.75rem] overflow-hidden rounded-xl bg-white shadow-lg shadow-slate-900/5 ring-1 ring-slate-500/10">
|
||||
@@ -337,11 +318,11 @@ function FeaturesMobile() {
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function FeaturesDesktop() {
|
||||
const t = useTranslations('HomePage')
|
||||
const t = useTranslations("HomePage");
|
||||
return (
|
||||
<Tab.Group as="div" className="hidden lg:mt-20 lg:block">
|
||||
{({ selectedIndex }) => (
|
||||
@@ -371,9 +352,8 @@ function FeaturesDesktop() {
|
||||
static
|
||||
key={feature.summary}
|
||||
className={cn(
|
||||
'px-5 transition duration-500 ease-in-out ui-not-focus-visible:outline-none',
|
||||
featureIndex !== selectedIndex &&
|
||||
'opacity-60',
|
||||
"px-5 transition duration-500 ease-in-out ui-not-focus-visible:outline-none",
|
||||
featureIndex !== selectedIndex && "opacity-60",
|
||||
)}
|
||||
style={{
|
||||
transform: `translateX(-${selectedIndex * 100}%)`,
|
||||
@@ -396,11 +376,11 @@ function FeaturesDesktop() {
|
||||
</>
|
||||
)}
|
||||
</Tab.Group>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function SecondaryFeatures() {
|
||||
const t = useTranslations('HomePage')
|
||||
const t = useTranslations("HomePage");
|
||||
return (
|
||||
<section
|
||||
id="secondary-features"
|
||||
@@ -410,15 +390,15 @@ export function SecondaryFeatures() {
|
||||
<Container className="max-w-[95rem]">
|
||||
<div className="mx-auto max-w-2xl md:text-center">
|
||||
<h2 className="font-display text-3xl tracking-tight text-primary sm:text-4xl">
|
||||
{t('secondaryFeatures.title')}
|
||||
{t("secondaryFeatures.title")}
|
||||
</h2>
|
||||
<p className="mt-4 text-lg tracking-tight text-muted-foreground">
|
||||
{t('secondaryFeatures.des')}
|
||||
{t("secondaryFeatures.des")}
|
||||
</p>
|
||||
</div>
|
||||
<FeaturesMobile />
|
||||
<FeaturesDesktop />
|
||||
</Container>
|
||||
</section>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Link } from '@/i18n/routing'
|
||||
import { Footer } from './Footer'
|
||||
import { Header } from './Header'
|
||||
import { useTranslations } from 'next-intl'
|
||||
import { Link } from "@/i18n/routing";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { Footer } from "./Footer";
|
||||
import { Header } from "./Header";
|
||||
|
||||
export function SlimLayout() {
|
||||
const t = useTranslations('404')
|
||||
const t = useTranslations("404");
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
@@ -12,16 +12,12 @@ export function SlimLayout() {
|
||||
</div>
|
||||
<main className="flex flex-auto items-center justify-center text-center">
|
||||
<div>
|
||||
<h1 className="mb-4 text-6xl font-semibold text-primary">
|
||||
404
|
||||
</h1>
|
||||
<p className="mb-4 text-lg text-muted-foreground">
|
||||
{t('title')}
|
||||
</p>
|
||||
<h1 className="mb-4 text-6xl font-semibold text-primary">404</h1>
|
||||
<p className="mb-4 text-lg text-muted-foreground">{t("title")}</p>
|
||||
<p className="mt-4 text-muted-foreground">
|
||||
{t('des')}{' '}
|
||||
{t("des")}{" "}
|
||||
<Link href="/" className="text-primary">
|
||||
{t('action')}
|
||||
{t("action")}
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
@@ -30,5 +26,5 @@ export function SlimLayout() {
|
||||
<Footer />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
'use client'
|
||||
"use client";
|
||||
|
||||
import * as React from 'react'
|
||||
import * as SelectPrimitive from '@radix-ui/react-select'
|
||||
import { Check, ChevronDown, ChevronUp } from 'lucide-react'
|
||||
import * as SelectPrimitive from "@radix-ui/react-select";
|
||||
import { Check, ChevronDown, ChevronUp } from "lucide-react";
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from '@/lib/utils'
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const Select = SelectPrimitive.Root
|
||||
const Select = SelectPrimitive.Root;
|
||||
|
||||
const SelectGroup = SelectPrimitive.Group
|
||||
const SelectGroup = SelectPrimitive.Group;
|
||||
|
||||
const SelectValue = SelectPrimitive.Value
|
||||
const SelectValue = SelectPrimitive.Value;
|
||||
|
||||
const SelectTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Trigger>,
|
||||
@@ -19,7 +19,7 @@ const SelectTrigger = React.forwardRef<
|
||||
<SelectPrimitive.Trigger
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1',
|
||||
"flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -29,8 +29,8 @@ const SelectTrigger = React.forwardRef<
|
||||
<ChevronDown className="h-4 w-4 opacity-50" />
|
||||
</SelectPrimitive.Icon>
|
||||
</SelectPrimitive.Trigger>
|
||||
))
|
||||
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
|
||||
));
|
||||
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
|
||||
|
||||
const SelectScrollUpButton = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
|
||||
@@ -39,15 +39,15 @@ const SelectScrollUpButton = React.forwardRef<
|
||||
<SelectPrimitive.ScrollUpButton
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'flex cursor-default items-center justify-center py-1',
|
||||
"flex cursor-default items-center justify-center py-1",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ChevronUp className="h-4 w-4" />
|
||||
</SelectPrimitive.ScrollUpButton>
|
||||
))
|
||||
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName
|
||||
));
|
||||
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
|
||||
|
||||
const SelectScrollDownButton = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
|
||||
@@ -56,28 +56,28 @@ const SelectScrollDownButton = React.forwardRef<
|
||||
<SelectPrimitive.ScrollDownButton
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'flex cursor-default items-center justify-center py-1',
|
||||
"flex cursor-default items-center justify-center py-1",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ChevronDown className="h-4 w-4" />
|
||||
</SelectPrimitive.ScrollDownButton>
|
||||
))
|
||||
));
|
||||
SelectScrollDownButton.displayName =
|
||||
SelectPrimitive.ScrollDownButton.displayName
|
||||
SelectPrimitive.ScrollDownButton.displayName;
|
||||
|
||||
const SelectContent = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
|
||||
>(({ className, children, position = 'popper', ...props }, ref) => (
|
||||
>(({ className, children, position = "popper", ...props }, ref) => (
|
||||
<SelectPrimitive.Portal>
|
||||
<SelectPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
||||
position === 'popper' &&
|
||||
'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
|
||||
"relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||
position === "popper" &&
|
||||
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
||||
className,
|
||||
)}
|
||||
position={position}
|
||||
@@ -86,9 +86,9 @@ const SelectContent = React.forwardRef<
|
||||
<SelectScrollUpButton />
|
||||
<SelectPrimitive.Viewport
|
||||
className={cn(
|
||||
'p-1',
|
||||
position === 'popper' &&
|
||||
'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]',
|
||||
"p-1",
|
||||
position === "popper" &&
|
||||
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]",
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
@@ -96,8 +96,8 @@ const SelectContent = React.forwardRef<
|
||||
<SelectScrollDownButton />
|
||||
</SelectPrimitive.Content>
|
||||
</SelectPrimitive.Portal>
|
||||
))
|
||||
SelectContent.displayName = SelectPrimitive.Content.displayName
|
||||
));
|
||||
SelectContent.displayName = SelectPrimitive.Content.displayName;
|
||||
|
||||
const SelectLabel = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Label>,
|
||||
@@ -105,11 +105,11 @@ const SelectLabel = React.forwardRef<
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SelectPrimitive.Label
|
||||
ref={ref}
|
||||
className={cn('py-1.5 pl-8 pr-2 text-sm font-semibold', className)}
|
||||
className={cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
SelectLabel.displayName = SelectPrimitive.Label.displayName
|
||||
));
|
||||
SelectLabel.displayName = SelectPrimitive.Label.displayName;
|
||||
|
||||
const SelectItem = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Item>,
|
||||
@@ -118,7 +118,7 @@ const SelectItem = React.forwardRef<
|
||||
<SelectPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -131,8 +131,8 @@ const SelectItem = React.forwardRef<
|
||||
|
||||
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
||||
</SelectPrimitive.Item>
|
||||
))
|
||||
SelectItem.displayName = SelectPrimitive.Item.displayName
|
||||
));
|
||||
SelectItem.displayName = SelectPrimitive.Item.displayName;
|
||||
|
||||
const SelectSeparator = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Separator>,
|
||||
@@ -140,11 +140,11 @@ const SelectSeparator = React.forwardRef<
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SelectPrimitive.Separator
|
||||
ref={ref}
|
||||
className={cn('-mx-1 my-1 h-px bg-muted', className)}
|
||||
className={cn("-mx-1 my-1 h-px bg-muted", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
SelectSeparator.displayName = SelectPrimitive.Separator.displayName
|
||||
));
|
||||
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
|
||||
|
||||
export {
|
||||
Select,
|
||||
@@ -157,4 +157,4 @@ export {
|
||||
SelectSeparator,
|
||||
SelectScrollUpButton,
|
||||
SelectScrollDownButton,
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { notFound } from 'next/navigation'
|
||||
import { getRequestConfig } from 'next-intl/server'
|
||||
import { routing } from './routing'
|
||||
import { getRequestConfig } from "next-intl/server";
|
||||
import { notFound } from "next/navigation";
|
||||
import { routing } from "./routing";
|
||||
|
||||
export default getRequestConfig(async ({ locale }) => {
|
||||
// Validate that the incoming `locale` parameter is valid
|
||||
if (!routing.locales.includes(locale as any)) notFound()
|
||||
// Validate that the incoming `locale` parameter is valid
|
||||
if (!routing.locales.includes(locale as any)) notFound();
|
||||
|
||||
return {
|
||||
messages: (await import(`../locales/${locale}.json`)).default,
|
||||
}
|
||||
})
|
||||
return {
|
||||
messages: (await import(`../locales/${locale}.json`)).default,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { defineRouting } from 'next-intl/routing'
|
||||
import { createSharedPathnamesNavigation } from 'next-intl/navigation'
|
||||
import { createSharedPathnamesNavigation } from "next-intl/navigation";
|
||||
import { defineRouting } from "next-intl/routing";
|
||||
|
||||
export const routing = defineRouting({
|
||||
// A list of all locales that are supported
|
||||
locales: ['en', 'zh-Hans'],
|
||||
locales: ["en", "zh-Hans"],
|
||||
|
||||
// Used when no locale matches
|
||||
defaultLocale: 'en',
|
||||
localePrefix: 'as-needed',
|
||||
})
|
||||
defaultLocale: "en",
|
||||
localePrefix: "as-needed",
|
||||
});
|
||||
|
||||
// Lightweight wrappers around Next.js' navigation APIs
|
||||
// that will consider the routing configuration
|
||||
export const { Link, redirect, usePathname, useRouter } =
|
||||
createSharedPathnamesNavigation(routing)
|
||||
createSharedPathnamesNavigation(routing);
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
"support": "Support",
|
||||
"discord": "Discord",
|
||||
"i18nButtonPlaceholder": "Language",
|
||||
"i18nEn":"English",
|
||||
"i18nZh-Hans":"简体中文"
|
||||
"i18nEn": "English",
|
||||
"i18nZh-Hans": "简体中文"
|
||||
},
|
||||
"hero": {
|
||||
"deploy": "Deploy",
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
"support": "赞助",
|
||||
"discord": "Discord",
|
||||
"i18nButtonPlaceholder": "语言",
|
||||
"i18nEn":"English",
|
||||
"i18nZh-Hans":"简体中文"
|
||||
"i18nEn": "English",
|
||||
"i18nZh-Hans": "简体中文"
|
||||
},
|
||||
"hero": {
|
||||
"deploy": "部署在",
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import createMiddleware from 'next-intl/middleware'
|
||||
import { routing } from './i18n/routing'
|
||||
import createMiddleware from "next-intl/middleware";
|
||||
import { routing } from "./i18n/routing";
|
||||
|
||||
export default createMiddleware(routing)
|
||||
export default createMiddleware(routing);
|
||||
|
||||
export const config = {
|
||||
// Match only internationalized pathnames
|
||||
matcher: ['/((?!_next|.*\\..*).*)']
|
||||
}
|
||||
matcher: ["/((?!_next|.*\\..*).*)"],
|
||||
};
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
const createNextIntlPlugin = require('next-intl/plugin')
|
||||
const createNextIntlPlugin = require("next-intl/plugin");
|
||||
|
||||
const withNextIntl = createNextIntlPlugin()
|
||||
const withNextIntl = createNextIntlPlugin();
|
||||
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
eslint: {
|
||||
ignoreDuringBuilds: true,
|
||||
},
|
||||
typescript: {
|
||||
ignoreBuildErrors: true,
|
||||
},
|
||||
}
|
||||
eslint: {
|
||||
ignoreDuringBuilds: true,
|
||||
},
|
||||
typescript: {
|
||||
ignoreBuildErrors: true,
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = withNextIntl(nextConfig)
|
||||
module.exports = withNextIntl(nextConfig);
|
||||
|
||||
@@ -4,5 +4,5 @@ module.exports = {
|
||||
semi: false,
|
||||
tabWidth: 4,
|
||||
useTabs: true,
|
||||
plugins: ['prettier-plugin-tailwindcss'],
|
||||
}
|
||||
plugins: ["prettier-plugin-tailwindcss"],
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user