mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
Feat/monorepo (#292)
* feat(create-turbo): apply official-starter transform * refactor: move folder * wip: monorepo * feat: add builf * refactor: add pr * update * add .env * refactor: update build * refactor: update build docker * refactor: add progress plain * refactor: remove node pty * refactor: remove * remove * refactor: update * refacotr: uopdate * refactor: add remix app * add env * refactor: add pnpm start * refactor: remove * refactor: remove folders * refactor: remove .dockerfile * chore: update biome * test * choe: add husky * remove .docker folder * feat: add docs website * refactor: add husky * chore(version): bump version * refactor: add new changes * refactor: update circle path * refactor: update * refactor: update * refactor: update dockerfile * refactor: update dockerfile * refactor: update command * refactor: update * refactor: update dockerfile * refactor: add tsx * refactor: update dockerfile * refactor: add deps * refactor: up[date * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: yuodate * refactor: remove * refactor: uncomment * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: updare * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: imprt * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: remove * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: change path * refactor: update * refactor: update * refactor: upoadte * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: add * refactor: update * refactor: update * refactor: add * refactor: update * refactor: remove * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: removed * refactor: update * refactor: update * refactor: update * refactor: add config * refactor: update * refactor: add * refactor: update * refactor: update * refactor: remove * refactor: update * refactor: remove * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: add docs * refactor: update * refactor: add website * refactor: update * refactor: update * refactor: update * refactor: update * refactor: add ignore builds * refactor: update * refactor: update * refactor: add * refactor: update * refactor: update * refactor: remove needs * refactor: update * refactor: update * refactor: add config * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: remove * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: add * refactor: update * refactor: update * refactor: update * refactor: update * refactor: upodate * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update * refactor: update package json * refactor: add biome * refactor: add sponsors * refactor: update * refactor: update * refactor: remove * refactor: update * refactor: update * refactor: update * refactor: update scripts * refactor: remove * refactor: update * refactor: remove --------- Co-authored-by: Turbobot <turbobot@vercel.com>
This commit is contained in:
56
apps/website/components/CallToAction.tsx
Normal file
56
apps/website/components/CallToAction.tsx
Normal file
File diff suppressed because one or more lines are too long
13
apps/website/components/Container.tsx
Normal file
13
apps/website/components/Container.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import clsx from "clsx";
|
||||
|
||||
export function Container({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentPropsWithoutRef<"div">) {
|
||||
return (
|
||||
<div
|
||||
className={clsx("mx-auto max-w-7xl px-4 sm:px-6 lg:px-8", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
101
apps/website/components/Faqs.tsx
Normal file
101
apps/website/components/Faqs.tsx
Normal file
@@ -0,0 +1,101 @@
|
||||
import { Container } from "./Container";
|
||||
|
||||
const faqs = [
|
||||
[
|
||||
{
|
||||
question: "What is dokploy?",
|
||||
answer:
|
||||
"Dokploy is a stable, easy-to-use deployment solution designed to simplify the application management process. Think of Dokploy as a free alternative self-hostable solution to platforms like Heroku, Vercel, and Netlify.",
|
||||
},
|
||||
{
|
||||
question: "Why Choose Dokploy?",
|
||||
answer: "Simplicity, Flexibility, and Fast",
|
||||
},
|
||||
{
|
||||
question: "Is free?",
|
||||
answer:
|
||||
"Yes, dokploy is totally free. You can use it for personal projects, small teams, or even for large-scale applications.",
|
||||
},
|
||||
{
|
||||
question: "Is it open source?",
|
||||
answer: "Yes, dokploy is open source and free to use.",
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
question: "What type of applications can i deploy with dokploy?",
|
||||
answer:
|
||||
"Dokploy is a great choice for any type of application. You can deploy your code to dokploy and manage it from the dashboard. We support a wide range of languages and frameworks, so you can choose the one that best fits your needs.",
|
||||
},
|
||||
{
|
||||
question: "How do I request a feature or report a bug?",
|
||||
answer:
|
||||
"Currently we are working on fixing bug fixes, but we will be releasing new features soon. You can also request features or report bugs.",
|
||||
},
|
||||
{
|
||||
question: "Do you track the usage of Dokploy?",
|
||||
answer: "No, we don't track any usage data.",
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
question:
|
||||
"Are there any user forums or communities where I can interact with other users?",
|
||||
answer:
|
||||
"Yes, we have active github discussions where you can share ideas, ask for help, and connect with other users.",
|
||||
},
|
||||
{
|
||||
question: "What types of applications can I deploy with Dokploy?",
|
||||
answer:
|
||||
"Dokploy supports a variety of applications, including those built with Docker, as well as applications from any Git repository, offering custom builds with Nixpacks, Dockerfiles, or Buildpacks like Heroku and Paketo.",
|
||||
},
|
||||
{
|
||||
question: "How does Dokploy handle database management?",
|
||||
answer:
|
||||
"Dokploy supports multiple database systems including Postgres, MySQL, MariaDB, MongoDB, and Redis, providing tools for easy deployment and management directly from the dashboard.",
|
||||
},
|
||||
],
|
||||
];
|
||||
|
||||
export function Faqs() {
|
||||
return (
|
||||
<section
|
||||
id="faqs"
|
||||
aria-labelledby="faq-title"
|
||||
className="relative overflow-hidden bg-black py-20 sm:py-32"
|
||||
>
|
||||
<Container className="relative">
|
||||
<div className="mx-auto max-w-2xl lg:mx-0">
|
||||
<h2
|
||||
id="faq-title"
|
||||
className="font-display text-3xl tracking-tight text-primary sm:text-4xl"
|
||||
>
|
||||
Frequently asked questions
|
||||
</h2>
|
||||
<p className="mt-4 text-lg tracking-tight text-muted-foreground">
|
||||
If you can’t find what you’re looking for, email our support team
|
||||
and if you’re lucky someone will get back to you.
|
||||
</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">
|
||||
{faqs.map((column, columnIndex) => (
|
||||
<li key={columnIndex}>
|
||||
<ul className="flex flex-col gap-y-8">
|
||||
{column.map((faq, faqIndex) => (
|
||||
<li key={faqIndex}>
|
||||
<h3 className="font-display text-lg leading-7 text-primary">
|
||||
{faq.question}
|
||||
</h3>
|
||||
<p className="mt-4 text-sm text-muted-foreground">
|
||||
{faq.answer}
|
||||
</p>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</Container>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
67
apps/website/components/Footer.tsx
Normal file
67
apps/website/components/Footer.tsx
Normal file
@@ -0,0 +1,67 @@
|
||||
import Link from "next/link";
|
||||
|
||||
import { Container } from "./Container";
|
||||
import { NavLink } from "./NavLink";
|
||||
import { Logo } from "./shared/Logo";
|
||||
|
||||
export function Footer() {
|
||||
return (
|
||||
<footer className="bg-black">
|
||||
<Container>
|
||||
<div className="py-16">
|
||||
<div className="flex flex-col gap-2 items-center">
|
||||
<Logo className="mx-auto h-10 w-auto" />
|
||||
<span className="text-center text-sm font-medium text-primary">
|
||||
Dokploy
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<nav className="mt-10 text-sm" aria-label="quick links">
|
||||
<div className="-my-1 flex justify-center gap-6 flex-wrap">
|
||||
<NavLink href="/#features">Features</NavLink>
|
||||
<NavLink href="/#faqs">Faqs</NavLink>
|
||||
<NavLink
|
||||
href="https://docs.dokploy.com/get-started/introduction"
|
||||
target="_blank"
|
||||
>
|
||||
Docs
|
||||
</NavLink>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
<div className="flex flex-col items-center border-t border-slate-400/10 py-10 sm:flex-row-reverse sm:justify-between">
|
||||
<div className="flex gap-x-6">
|
||||
<Link
|
||||
href="https://twitter.com/Siumauricio"
|
||||
className="group"
|
||||
aria-label="Dokploy on Twitter"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
className="h-6 w-6 fill-muted-foreground group-hover:fill-muted-foreground/70"
|
||||
>
|
||||
<path d="M8.29 20.251c7.547 0 11.675-6.253 11.675-11.675 0-.178 0-.355-.012-.53A8.348 8.348 0 0 0 22 5.92a8.19 8.19 0 0 1-2.357.646 4.118 4.118 0 0 0 1.804-2.27 8.224 8.224 0 0 1-2.605.996 4.107 4.107 0 0 0-6.993 3.743 11.65 11.65 0 0 1-8.457-4.287 4.106 4.106 0 0 0 1.27 5.477A4.073 4.073 0 0 1 2.8 9.713v.052a4.105 4.105 0 0 0 3.292 4.022 4.093 4.093 0 0 1-1.853.07 4.108 4.108 0 0 0 3.834 2.85A8.233 8.233 0 0 1 2 18.407a11.615 11.615 0 0 0 6.29 1.84" />
|
||||
</svg>
|
||||
</Link>
|
||||
<Link
|
||||
href="https://github.com/dokploy/dokploy"
|
||||
className="group"
|
||||
aria-label="Dokploy on GitHub"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
className="h-6 w-6 fill-muted-foreground group-hover:fill-muted-foreground/70"
|
||||
>
|
||||
<path d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0 1 12 6.844a9.59 9.59 0 0 1 2.504.337c1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.02 10.02 0 0 0 22 12.017C22 6.484 17.522 2 12 2Z" />
|
||||
</svg>
|
||||
</Link>
|
||||
</div>
|
||||
<p className="mt-6 text-sm text-muted-foreground sm:mt-0">
|
||||
Copyright © {new Date().getFullYear()} Dokploy. All rights
|
||||
reserved.
|
||||
</p>
|
||||
</div>
|
||||
</Container>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
180
apps/website/components/Header.tsx
Normal file
180
apps/website/components/Header.tsx
Normal file
@@ -0,0 +1,180 @@
|
||||
"use client";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Popover, Transition } from "@headlessui/react";
|
||||
import { HeartIcon } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { Fragment } 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;
|
||||
}) {
|
||||
return (
|
||||
<Popover.Button
|
||||
onClick={() => {
|
||||
trackGAEvent({
|
||||
action: "Nav Link Clicked",
|
||||
category: "Navigation",
|
||||
label: href,
|
||||
});
|
||||
}}
|
||||
as={Link}
|
||||
href={href}
|
||||
target={target}
|
||||
className="block w-full p-2"
|
||||
>
|
||||
{children}
|
||||
</Popover.Button>
|
||||
);
|
||||
}
|
||||
|
||||
function MobileNavIcon({ open }: { open: boolean }) {
|
||||
return (
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
className="h-3.5 w-3.5 overflow-visible stroke-muted-foreground"
|
||||
fill="none"
|
||||
strokeWidth={2}
|
||||
strokeLinecap="round"
|
||||
>
|
||||
<path
|
||||
d="M0 1H14M0 7H14M0 13H14"
|
||||
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",
|
||||
)}
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function MobileNavigation() {
|
||||
return (
|
||||
<Popover>
|
||||
<Popover.Button
|
||||
className="relative z-10 flex h-8 w-8 items-center justify-center ui-not-focus-visible:outline-none"
|
||||
aria-label="Toggle Navigation"
|
||||
>
|
||||
{({ open }) => <MobileNavIcon open={open} />}
|
||||
</Popover.Button>
|
||||
<Transition.Root>
|
||||
<Transition.Child
|
||||
as={Fragment as any}
|
||||
enter="duration-150 ease-out"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="duration-150 ease-in"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<Popover.Overlay className="fixed inset-0 bg-background/50" />
|
||||
</Transition.Child>
|
||||
|
||||
<Transition.Child
|
||||
as={Fragment as any}
|
||||
enter="duration-150 ease-out"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="duration-100 ease-in"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<Popover.Panel
|
||||
as="div"
|
||||
className="absolute inset-x-0 top-full mt-4 flex origin-top flex-col rounded-2xl bg-background border border-border p-4 text-lg tracking-tight text-primary shadow-xl ring-1 ring-border/5"
|
||||
>
|
||||
<MobileNavLink href="/#features">Features</MobileNavLink>
|
||||
<MobileNavLink href="/#testimonials">Testimonials</MobileNavLink>
|
||||
<MobileNavLink href="/#faqs">Faqs</MobileNavLink>
|
||||
<MobileNavLink
|
||||
href="https://docs.dokploy.com/get-started/introduction"
|
||||
target="_blank"
|
||||
>
|
||||
Docs
|
||||
</MobileNavLink>
|
||||
</Popover.Panel>
|
||||
</Transition.Child>
|
||||
</Transition.Root>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
|
||||
export function Header() {
|
||||
return (
|
||||
<header className="py-10 bg-background">
|
||||
<Container>
|
||||
<nav className="relative z-50 flex justify-between">
|
||||
<div className="flex items-center md:gap-x-12">
|
||||
<Link href="#" aria-label="Home">
|
||||
<Logo className="h-10 w-auto" />
|
||||
</Link>
|
||||
<div className="hidden md:flex md:gap-x-6">
|
||||
<NavLink href="/#features">Features</NavLink>
|
||||
{/* <NavLink href="/#testimonials">Testimonials</NavLink> */}
|
||||
<NavLink href="/#faqs">Faqs</NavLink>
|
||||
<NavLink
|
||||
href="https://docs.dokploy.com/get-started/introduction"
|
||||
target="_blank"
|
||||
>
|
||||
Docs
|
||||
</NavLink>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-x-2 md:gap-x-5">
|
||||
<Link
|
||||
className={buttonVariants({
|
||||
variant: "outline",
|
||||
className: " flex items-center gap-2 !rounded-full",
|
||||
})}
|
||||
href="https://opencollective.com/dokploy"
|
||||
target="_blank"
|
||||
>
|
||||
<span className="text-sm font-semibold">Support </span>
|
||||
<HeartIcon className="size-4 text-red-500 fill-red-600 animate-heartbeat " />
|
||||
</Link>
|
||||
{/* @ts-expect-error */}
|
||||
<Button
|
||||
className="rounded-full bg-[#5965F2] hover:bg-[#4A55E0]"
|
||||
asChild
|
||||
>
|
||||
<Link
|
||||
href="https://discord.gg/2tBnJ3jDJc"
|
||||
aria-label="Dokploy on GitHub"
|
||||
target="_blank"
|
||||
className="flex flex-row gap-2 items-center text-white"
|
||||
>
|
||||
<svg
|
||||
role="img"
|
||||
className="h-6 w-6 fill-white"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<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>
|
||||
Discord
|
||||
</Link>
|
||||
</Button>
|
||||
<div className="-mr-1 md:hidden">
|
||||
<MobileNavigation />
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</Container>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
181
apps/website/components/Hero.tsx
Normal file
181
apps/website/components/Hero.tsx
Normal file
File diff suppressed because one or more lines are too long
33
apps/website/components/NavLink.tsx
Normal file
33
apps/website/components/NavLink.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { trackGAEvent } from "./analitycs";
|
||||
|
||||
export function NavLink({
|
||||
href,
|
||||
children,
|
||||
target,
|
||||
}: {
|
||||
href: string;
|
||||
children: React.ReactNode;
|
||||
target?: string;
|
||||
}) {
|
||||
return (
|
||||
<div>
|
||||
<Link
|
||||
href={href}
|
||||
onClick={() =>
|
||||
trackGAEvent({
|
||||
action: "Nav Link Clicked",
|
||||
category: "Navigation",
|
||||
label: href,
|
||||
})
|
||||
}
|
||||
target={target}
|
||||
className="inline-block self-center rounded-lg px-2.5 py-1.5 text-sm text-popover-foreground font-medium transition-colors hover:text-primary hover:bg-secondary"
|
||||
>
|
||||
{children}
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
199
apps/website/components/PrimaryFeatures.tsx
Normal file
199
apps/website/components/PrimaryFeatures.tsx
Normal file
@@ -0,0 +1,199 @@
|
||||
"use client";
|
||||
|
||||
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";
|
||||
|
||||
const features = [
|
||||
{
|
||||
title: "Projects",
|
||||
description:
|
||||
"Manage and organize all your projects in one place, keeping detailed track of progress and resource allocation.",
|
||||
image: "/primary/projects.png",
|
||||
},
|
||||
{
|
||||
title: "Applications & Databases",
|
||||
description:
|
||||
"Centralize control over your applications and databases for enhanced security and efficiency, simplifying access and management across your infrastructure.",
|
||||
image: "/primary/applications.png",
|
||||
},
|
||||
{
|
||||
title: "Docker Compose",
|
||||
description:
|
||||
"Native Docker Compose support for manage complex applications and services with ease.",
|
||||
image: "/primary/compose.png",
|
||||
},
|
||||
{
|
||||
title: "Multi Node",
|
||||
description:
|
||||
"Scale applications to multiples nodes using docker swarm to manage the cluster.",
|
||||
image: "/primary/multinode.png",
|
||||
},
|
||||
{
|
||||
title: "Monitoring",
|
||||
description:
|
||||
"Monitor your systems' performance and health in real time, ensuring continuous and uninterrupted operation.",
|
||||
image: "/primary/monitoring.png",
|
||||
},
|
||||
{
|
||||
title: "Backups",
|
||||
description:
|
||||
"Implement automatic and secure backup solutions to protect your critical data and restore it quickly when necessary.",
|
||||
image: "/primary/backups.png",
|
||||
},
|
||||
];
|
||||
|
||||
export function PrimaryFeatures() {
|
||||
const [tabOrientation, setTabOrientation] = useState<
|
||||
"horizontal" | "vertical"
|
||||
>("horizontal");
|
||||
|
||||
useEffect(() => {
|
||||
const lgMediaQuery = window.matchMedia("(min-width: 1024px)");
|
||||
|
||||
function onMediaQueryChange({ matches }: { matches: boolean }) {
|
||||
setTabOrientation(matches ? "vertical" : "horizontal");
|
||||
}
|
||||
|
||||
onMediaQueryChange(lgMediaQuery);
|
||||
lgMediaQuery.addEventListener("change", onMediaQueryChange);
|
||||
|
||||
return () => {
|
||||
lgMediaQuery.removeEventListener("change", onMediaQueryChange);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const [isMounted, setIsMounted] = useState(false);
|
||||
|
||||
// Cambiar isMounted a true después del primer render
|
||||
useEffect(() => {
|
||||
setIsMounted(true);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<section
|
||||
id="features"
|
||||
aria-label="Features for running your books"
|
||||
className="relative overflow-hidden bg-black pb-28 pt-20 sm:py-32"
|
||||
>
|
||||
{/* <div class="absolute inset-0 h-full w-full bg-background bg-[linear-gradient(to_right,#80808012_1px,transparent_1px),linear-gradient(to_bottom,#80808012_1px,transparent_1px)] bg-[size:24px_24px]" /> */}
|
||||
|
||||
{/* <Image
|
||||
className="absolute left-1/2 top-1/2 max-w-none translate-x-[-44%] translate-y-[-42%]"
|
||||
src={backgroundImage}
|
||||
alt=""
|
||||
width={2245}
|
||||
height={1636}
|
||||
unoptimized
|
||||
/> */}
|
||||
<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">
|
||||
Comprehensive Control for Your Digital Ecosystem
|
||||
</h2>
|
||||
<p className="mt-6 text-lg tracking-tight text-muted-foreground">
|
||||
Simplify your project and data management, ensure robust monitoring,
|
||||
and secure your backups—all without the fuss over minute details.
|
||||
</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"}
|
||||
>
|
||||
{({ selectedIndex }) => (
|
||||
<>
|
||||
<div className="-mx-4 flex overflow-x-auto pb-4 sm:mx-0 sm:overflow-visible sm:pb-0 lg:col-span-5">
|
||||
<Tab.List
|
||||
aria-description="primary feature tabs"
|
||||
aria-roledescription="primary feature tabs"
|
||||
className="relative z-10 flex gap-x-4 whitespace-nowrap px-4 sm:mx-auto sm:px-0 lg:mx-0 lg:block lg:gap-x-0 lg:gap-y-1 lg:whitespace-normal"
|
||||
>
|
||||
{features.map((feature, featureIndex) => (
|
||||
<motion.div
|
||||
layout
|
||||
initial={false}
|
||||
key={`feature-${featureIndex}`}
|
||||
className={cn(
|
||||
"group relative rounded-full px-4 py-1 lg:rounded-l-xl lg:rounded-r-none lg:p-6 transition-colors ",
|
||||
)}
|
||||
>
|
||||
<AnimatePresence>
|
||||
{selectedIndex === featureIndex && (
|
||||
<motion.span
|
||||
layoutId="tab"
|
||||
className="absolute inset-0 z-10 bg-white/5 rounded-full mix-blend-difference lg:rounded-l-xl lg:rounded-r-none"
|
||||
initial={{ opacity: 1 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
transition={{
|
||||
type: "spring",
|
||||
bounce: 0.2,
|
||||
duration: 0.5,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
<h3>
|
||||
<Tab
|
||||
className={cn(
|
||||
"font-display text-lg ui-not-focus-visible:outline-none text-primary",
|
||||
)}
|
||||
>
|
||||
<span className="absolute inset-0 rounded-full lg:rounded-l-xl lg:rounded-r-none" />
|
||||
{feature.title}
|
||||
</Tab>
|
||||
</h3>
|
||||
<p
|
||||
className={cn(
|
||||
"mt-2 hidden text-sm lg:block text-muted-foreground",
|
||||
)}
|
||||
>
|
||||
{feature.description}
|
||||
</p>
|
||||
</motion.div>
|
||||
))}
|
||||
</Tab.List>
|
||||
</div>
|
||||
<Tab.Panels className="lg:col-span-7">
|
||||
{features.map((feature, index) => (
|
||||
<Tab.Panel key={`panel-${index}`}>
|
||||
<div className="relative sm:px-6 lg:hidden">
|
||||
<div className="absolute -inset-x-4 bottom-[-4.25rem] top-[-6.5rem] bg-white/10 ring-1 ring-inset ring-white/10 sm:inset-x-0 sm:rounded-t-xl" />
|
||||
<p className="relative mx-auto max-w-2xl text-base text-white sm:text-center">
|
||||
{feature.description}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<motion.div
|
||||
key={feature.title}
|
||||
initial={isMounted ? { opacity: 0.8, x: 50 } : {}}
|
||||
animate={isMounted ? { opacity: 1, x: 0 } : {}}
|
||||
exit={{ opacity: 0, x: -50 }}
|
||||
transition={{
|
||||
type: "spring",
|
||||
bounce: 0.2,
|
||||
duration: 0.6,
|
||||
}}
|
||||
className="mt-10 h-[24rem] lg:h-[40rem] w-[45rem] overflow-hidden rounded-xl shadow-xl border sm:w-auto lg:mt-0 lg:w-[67.8125rem]"
|
||||
>
|
||||
<img
|
||||
alt=""
|
||||
className="w-full"
|
||||
src={feature.image}
|
||||
srcSet={`${feature.image} 1x`}
|
||||
/>
|
||||
</motion.div>
|
||||
</Tab.Panel>
|
||||
))}
|
||||
</Tab.Panels>
|
||||
</>
|
||||
)}
|
||||
</Tab.Group>
|
||||
</Container>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
407
apps/website/components/SecondaryFeatures.tsx
Normal file
407
apps/website/components/SecondaryFeatures.tsx
Normal file
@@ -0,0 +1,407 @@
|
||||
"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";
|
||||
interface Feature {
|
||||
name: React.ReactNode;
|
||||
summary: string;
|
||||
description: string;
|
||||
image: string;
|
||||
icon: React.ComponentType;
|
||||
}
|
||||
|
||||
const features: Array<Feature> = [
|
||||
{
|
||||
name: "Open Source Templates",
|
||||
summary: "One click to deploy open source templates.",
|
||||
description:
|
||||
"Deploy open source templates with one click, powered by Docker Compose, (Plausible, Calcom, Pocketbase, etc.)",
|
||||
image: "/secondary/templates.png",
|
||||
icon: function ReportingIcon() {
|
||||
return (
|
||||
<>
|
||||
<Layers className="size-5 text-primary" />
|
||||
</>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Real-Time Traefik Configuration",
|
||||
summary:
|
||||
" Modify Traefik settings on-the-fly via a graphical interface or API.",
|
||||
description:
|
||||
"Users can adjust Traefik's configuration, including middleware, forwarding rules, and SSL certificates through an intuitive interface or API. This feature enables seamless traffic routing and security adjustments without the need to restart services",
|
||||
image: "/secondary/traefik.png",
|
||||
icon: function ReportingIcon() {
|
||||
return (
|
||||
<>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 340 456"
|
||||
className="size-6"
|
||||
>
|
||||
<path
|
||||
d="M65.412 155.072s24.668-21.474 97.677-21.474c66.978 0 85.8 15.194 104.918 21.474l-99.736 48.735-102.859-48.735z"
|
||||
fill="#c9781f"
|
||||
/>
|
||||
<g transform="matrix(.639262 0 0 .639262 -21.039129 -84.874827)">
|
||||
<path
|
||||
d="M118.946 476.458c.707 14.572 15.264 7.83 21.858 3.274 6.259-4.325 8.089-.73 8.638-9.266.36-5.61 1.007-11.22.688-16.853-9.464-.858-19.759 1.396-27.518 7.033-3.996 2.905-11.49 12.174-3.666 15.812"
|
||||
fill="#f6d2a2"
|
||||
/>
|
||||
<path
|
||||
d="M118.946 476.458c2.119-.788 4.364-1.348 5.802-3.264"
|
||||
fill="#c6b198"
|
||||
/>
|
||||
<path
|
||||
d="M152.588 302.861c-55.784-15.687-14.304-86.654 30.492-57.464l-30.492 57.464zm247.848-62.79c44.155-31.014 84.056 38.959 32.74 56.565l-32.74-56.565z"
|
||||
fill="#37abc8"
|
||||
/>
|
||||
<path
|
||||
d="M409.934 655.8c11.216 6.94 31.716 27.923 14.891 38.098-16.166 14.802-25.214-16.247-39.403-20.549 6.111-8.298 13.856-15.865 24.512-17.549zm-200.373 23.714c-13.164 2.037-20.574 13.914-31.548 19.945-10.341 6.166-14.297-1.974-15.229-3.627-1.621-.739-1.485.688-3.987-1.831-9.587-15.13 9.989-26.189 20.182-33.705 14.198-2.871 23.096 9.438 30.582 19.218z"
|
||||
fill="#f6d2a2"
|
||||
/>
|
||||
<path
|
||||
d="M154.916 283.26c-7.36-3.893-12.759-9.18-8.257-17.693 4.168-7.88 11.911-7.025 19.271-3.132l-11.014 20.825zm266.633-7.401c7.36-3.893 12.759-9.18 8.257-17.693-4.168-7.881-11.91-7.025-19.271-3.132l11.014 20.825z"
|
||||
fill="#077e91"
|
||||
/>
|
||||
<path
|
||||
d="M472.21 474.607c-.707 14.572-15.264 7.83-21.858 3.274-6.259-4.325-8.089-.73-8.638-9.265-.36-5.61-1.007-11.22-.688-16.853 9.464-.858 19.759 1.396 27.518 7.033 3.996 2.904 11.49 12.174 3.666 15.811"
|
||||
fill="#f6d2a2"
|
||||
/>
|
||||
<path
|
||||
d="M472.21 474.607c-2.119-.788-4.364-1.348-5.802-3.264"
|
||||
fill="#c6b198"
|
||||
/>
|
||||
<path
|
||||
d="M289.988 210.595c55.847 0 108.2 7.987 135.492 61.642 24.496 60.141 15.785 124.993 19.521 188.553 3.208 54.577 10.322 117.629-14.997 168.205-26.635 53.21-93.191 66.595-148.026 64.634-43.071-1.541-95.101-15.593-119.409-54.944-28.519-46.165-15.017-114.81-12.946-166.179 2.454-60.849-16.482-121.882 3.508-181.425 20.737-61.765 76.665-75.724 136.857-80.486"
|
||||
fill="#37abc8"
|
||||
/>
|
||||
<path
|
||||
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="214.64"
|
||||
cy="290.071"
|
||||
rx="3.234"
|
||||
ry="3.777"
|
||||
fill="#fff"
|
||||
/>
|
||||
<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" />
|
||||
<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
|
||||
d="M278.185 326.748c-11.156.951-20.276 14.216-14.475 24.71 7.682 13.9 24.828-1.23 35.507.188 12.291.252 22.361 12.996 32.233 2.304 10.979-11.892-4.727-23.474-17.002-28.652l-36.263 1.45z"
|
||||
fill="#f6d2a2"
|
||||
/>
|
||||
</g>
|
||||
<path
|
||||
d="M65.412 155.072s2.258 24.459 2.103 35.002 9.947 3.886 10.471 20.467-6.119 13.436-9.619 21.551-5.879 46.12-5.879 46.12 3.591 7.101 14.809 13.495 29.256 9.985 44.875 9.278 26.775-4.054 29.565-5.904 7.419-7.703 9.16-12.23 5.379-41.809 5.126-63.143-3.779-41.019-3.779-41.019l-96.833-23.616z"
|
||||
fill="#ef9325"
|
||||
/>
|
||||
<path
|
||||
d="M164.351 258.946c-45.996 5.135-99.366-9.06-99.366-9.06s.784-14.397 4.236-19.464c47.838 14.821 96.1 10.122 96.1 10.122.716 6.721.212 12.064-.969 18.401zm-5.789 28.375c-45.996 5.135-96.073-9.63-96.073-9.63s-.16-13.828 1.667-19.9c47.838 14.821 98.934 11.128 98.934 11.128-.43 7.296-1.456 14.477-4.528 18.402z"
|
||||
fill="#e5e5e5"
|
||||
/>
|
||||
<path
|
||||
d="M268.007 155.072l-.155 35.002c.157 10.543-6.123 3.886-6.654 20.467s6.196 13.436 9.74 21.551 5.953 46.12 5.953 46.12-3.637 7.101-14.995 13.495-29.623 9.985-45.439 9.278-27.112-4.054-29.937-5.904-7.512-7.703-9.275-12.23-5.447-41.809-5.19-63.143 3.826-41.019 3.826-41.019l92.127-23.616z"
|
||||
fill="#ef9325"
|
||||
/>
|
||||
<path
|
||||
d="M173.748 258.946c46.574 5.135 100.615-9.06 100.615-9.06s-.794-14.397-4.289-19.464c-48.439 14.821-97.307 10.122-97.307 10.122-.725 6.721-.214 12.064.981 18.401zm5.862 28.375c46.574 5.135 97.28-9.63 97.28-9.63s.162-13.828-1.688-19.9c-48.439 14.821-100.177 11.128-100.177 11.128.436 7.296 1.475 14.477 4.585 18.402z"
|
||||
fill="#e5e5e5"
|
||||
/>
|
||||
<path
|
||||
d="M286.587 199.932c-1.447 2.731-4.312 4.049-6.398 2.943l-1.672-.885c-2.087-1.105-2.606-4.216-1.158-6.947l32.593-61.519c1.447-2.731 4.312-4.049 6.398-2.943l1.672.885c2.087 1.105 2.606 4.216 1.158 6.947l-32.593 61.519z"
|
||||
fill="#d2e261"
|
||||
/>
|
||||
<path d="M271.195 228.985c-.843 1.591-3.218 1.985-5.305.879l-1.672-.885c-2.087-1.105-3.095-3.292-2.252-4.883l15.291-28.862c.843-1.591 3.218-1.985 5.305-.879l1.672.885c2.087 1.105 3.095 3.292 2.252 4.883l-15.291 28.862z" />
|
||||
<path
|
||||
d="M284.791 194.189l-1.672-.885c-2.087-1.105-4.222-1.165-4.768-.134l-.977 1.845c.547-1.032 2.681-.972 4.768.134l1.672.885c2.087 1.105 3.336 2.838 2.789 3.869l.977-1.845c.547-1.031-.703-2.764-2.789-3.869z"
|
||||
fill="#9b9b9b"
|
||||
/>
|
||||
<ellipse
|
||||
transform="matrix(.599116 .222975 -.222975 .599116 100.48325 -168.1724)"
|
||||
cx="456.838"
|
||||
cy="462.661"
|
||||
rx="11.224"
|
||||
ry="8.683"
|
||||
fill="#f6d2a2"
|
||||
/>
|
||||
<path
|
||||
d="M50.417 200.925c1.32 2.795 4.121 4.243 6.256 3.234l1.71-.808c2.135-1.009 2.796-4.092 1.476-6.887l-29.737-62.949c-1.32-2.795-4.121-4.243-6.256-3.234l-1.71.808c-2.135 1.009-2.796 4.092-1.476 6.887l29.737 62.949z"
|
||||
fill="#d2e261"
|
||||
/>
|
||||
<path d="M64.461 230.653c.769 1.628 3.123 2.13 5.259 1.122l1.71-.808c2.135-1.009 3.243-3.146 2.473-4.775l-13.952-29.533c-.769-1.628-3.123-2.13-5.259-1.122l-1.71.808c-2.135 1.009-3.243 3.146-2.473 4.775l13.952 29.533z" />
|
||||
<path
|
||||
d="M52.475 195.27l1.71-.808c2.135-1.009 4.27-.971 4.769.084l.892 1.888c-.499-1.055-2.634-1.093-4.769-.085l-1.71.808c-2.135 1.009-3.462 2.682-2.964 3.738l-.892-1.888c-.498-1.055.828-2.728 2.964-3.737z"
|
||||
fill="#9b9b9b"
|
||||
/>
|
||||
<ellipse
|
||||
transform="matrix(-.599116 .222975 -.222975 -.599116 252.76813 458.95304)"
|
||||
cx="137.56"
|
||||
cy="463.922"
|
||||
rx="11.224"
|
||||
ry="8.683"
|
||||
fill="#f6d2a2"
|
||||
/>
|
||||
<g transform="matrix(.639262 0 0 .639262 -21.039129 -84.874827)">
|
||||
<path
|
||||
d="M159.132 324.732c-2.218 4.033-7.45 5.414-11.687 3.084l-4.873-2.679c-4.237-2.33-5.873-7.488-3.656-11.521l50.714-92.23c2.218-4.033 7.45-5.414 11.687-3.084l4.873 2.679c4.237 2.33 5.33 6.689 3.656 11.521-17.765 51.258-50.714 92.23-50.714 92.23z"
|
||||
fill="#960000"
|
||||
/>
|
||||
<path
|
||||
d="M172.547 272.051c15.422-28.047 25.555-52.169 23.141-54.905l.057-.103c-.066-.036-.136-.06-.202-.096-.008-.005-.01-.021-.019-.026l-.007.014c-23.699-12.841-55.583-.124-71.334 28.522s-9.41 62.381 14.128 75.514l-.007.014c.009.005.023-.002.032.002.066.037.123.083.189.119l.057-.103c3.602.573 18.543-20.905 33.965-48.952z"
|
||||
fill="#595959"
|
||||
/>
|
||||
<path
|
||||
d="M426.693 324.925c2.1 4.095 7.291 5.627 11.593 3.42l4.948-2.538c4.302-2.206 6.087-7.315 3.987-11.41l-48.031-93.655c-2.1-4.095-7.291-5.627-11.593-3.42l-4.948 2.538c-4.302 2.206-5.521 6.533-3.987 11.41 16.279 51.749 48.031 93.655 48.031 93.655z"
|
||||
fill="#960000"
|
||||
/>
|
||||
<path
|
||||
d="M414.804 271.879c-14.606-28.48-24.039-52.885-21.547-55.55l-.054-.105c.067-.034.138-.056.205-.09.009-.005.011-.021.02-.025l.007.014c24.059-12.152 55.563 1.48 70.481 30.569s7.606 62.627-16.301 75.075l.007.014c-.009.004-.023-.003-.032.001-.067.035-.125.08-.192.114l-.054-.105c-3.618.468-17.934-21.432-32.54-49.912z"
|
||||
fill="#595959"
|
||||
/>
|
||||
<path
|
||||
d="M462.36 259.869s-17.746-44.446-38.326-67.945-221.937-31.512-255.003 0-45.533 67.945-45.533 67.945v-9.844s15.295-43.268 45.533-67.945 228.946-23.378 254.582 0 38.747 67.945 38.747 67.945v9.844z"
|
||||
fill="#353535"
|
||||
/>
|
||||
<g fill="#960000">
|
||||
<ellipse
|
||||
transform="matrix(-.8898 .4563 -.4563 -.8898 986.5333 258.1984)"
|
||||
cx="462.092"
|
||||
cy="248.211"
|
||||
rx="6.949"
|
||||
ry="16.975"
|
||||
/>
|
||||
<ellipse
|
||||
transform="matrix(.8763 .4818 -.4818 .8763 134.6121 -30.1261)"
|
||||
cx="125.962"
|
||||
cy="247.029"
|
||||
rx="6.949"
|
||||
ry="16.975"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g fill="#fff">
|
||||
<path
|
||||
d="M226.029 75.255h-23.784-18.854-28.918-16.336-32.168c-21.556 0-21.482 57.282-1.243 60.583l41.85-2.691c5.06 0 15.163-19.597 17.058-21.137s8.137-1.729 10.596 0 8.05 19.163 13.109 19.163l41.85 4.665c19.548-8.996 14.62-60.583-3.16-60.583z"
|
||||
opacity=".6"
|
||||
/>
|
||||
<path
|
||||
d="M137.846 87.099c-13.223-.295-33.913-.175-45.214-.083-1.895 6.213-4.008 14.079-2.96 21.717l48.445.05c9.554 0 17.587 1.803 25.058 3.756.184-.235.339-.408.46-.506 1.895-1.541 8.137-1.729 10.596 0 .636.447 1.485 1.955 2.477 3.931 3.814.804 7.635 1.36 11.605 1.41l52.462.047c2.077-6.94.545-14.38.08-21.372-15.921 0-41.224-.359-53.355-.359-17.093-.001-32.016-8.196-49.651-8.59z"
|
||||
opacity=".5"
|
||||
/>
|
||||
</g>
|
||||
<path d="M155.896 123.631c-.528-12.356 23.029-13.9 25.811-3.558 2.775 10.318-24.651 12.715-25.811 3.558-.926-7.311 0 0 0 0z" />
|
||||
<g transform="matrix(.163636 0 0 .163636 2.866751 26.432443)">
|
||||
<path
|
||||
d="M380.282 2504.931h17.342c7.054 0 12.737-.196 17.048-.588a60.88 60.88 0 0 0 13.227-2.645c4.507-1.372 7.838-3.527 9.994-6.466 2.155-3.135 3.233-7.152 3.233-12.051 0-5.487-1.862-10.288-5.585-14.403s-8.818-6.173-15.284-6.173h-1.764l-19.987.882h-3.233c-12.345 0-21.359-4.409-27.042-13.227-5.683-9.014-8.524-23.612-8.524-43.796v-160.193h54.083c12.933 0 19.399-5.584 19.399-16.754 0-5.29-1.666-9.601-4.997-12.933-3.135-3.331-7.936-4.997-14.403-4.997h-54.083v-85.24c0-6.858-1.96-12.051-5.879-15.578-3.919-3.723-8.818-5.584-14.697-5.585-6.271 0-12.051 2.254-17.342 6.76-5.095 4.507-7.838 9.896-8.23 16.166l-7.054 83.477H270.35c-6.467 0-11.365 1.666-14.697 4.997-3.331 3.136-4.997 7.251-4.997 12.345 0 5.291 1.666 9.504 4.997 12.639 3.527 3.136 8.524 4.703 14.991 4.703h35.86v169.305c0 30.373 6.662 52.908 19.987 67.604 13.325 14.501 31.255 21.751 53.79 21.751m149.319-4.115c7.25 0 13.521-2.155 18.812-6.466 5.487-4.311 8.23-10.386 8.23-18.224v-151.375c0-22.927 7.25-41.248 21.751-54.965 14.696-13.717 35.272-20.575 61.726-20.575 6.27 0 11.071-2.057 14.403-6.173 3.331-4.311 4.997-9.406 4.997-15.284 0-6.27-1.862-11.757-5.585-16.46-3.527-4.703-8.426-7.054-14.697-7.054-20.771 0-38.603 6.173-53.496 18.518-14.697 12.149-24.494 27.336-29.393 45.559l.294-35.272c0-7.446-2.645-13.227-7.936-17.342-5.095-4.311-11.268-6.466-18.518-6.467s-13.521 2.156-18.812 6.467c-5.291 4.115-7.936 10.092-7.936 17.93v242.494c0 7.838 2.547 13.913 7.642 18.224s11.267 6.466 18.518 6.466m719.005-.001c7.25 0 13.521-2.253 18.812-6.76 5.291-4.703 7.936-11.169 7.936-19.4v-228.385h49.674c12.541 0 18.811-5.682 18.812-17.048 0-5.095-1.568-9.308-4.703-12.639-2.94-3.331-7.642-4.997-14.109-4.997h-49.674v-22.927c0-15.872.588-28.413 1.764-37.623 1.372-9.405 3.821-16.558 7.348-21.457 3.723-5.094 7.936-8.328 12.639-9.7 4.703-1.567 11.267-2.351 19.693-2.351h24.984c5.878 0 10.385-1.959 13.521-5.879 3.331-3.919 4.997-8.622 4.997-14.109s-1.666-10.189-4.997-14.109c-3.135-4.115-7.544-6.172-13.227-6.173h-35.86c-26.258 0-46.833 7.447-61.726 22.339-14.893 14.697-22.339 39.583-22.339 74.659v37.329h-38.505c-6.467 0-11.267 1.666-14.403 4.997s-4.703 7.545-4.703 12.639c0 4.899 1.568 9.014 4.703 12.345 3.135 3.136 7.936 4.703 14.403 4.703h38.505v228.385c0 8.23 2.547 14.697 7.642 19.4 5.291 4.507 11.561 6.76 18.812 6.76m185.472-360.065c10.385 0 18.714-2.939 24.984-8.818 6.27-6.074 9.406-13.912 9.406-23.515 0-9.797-3.135-17.636-9.406-23.515-6.271-6.074-14.501-9.111-24.69-9.112-10.386 0-18.812 3.038-25.278 9.112-6.271 6.075-9.406 13.913-9.406 23.515s3.135 17.44 9.406 23.515c6.27 5.879 14.599 8.818 24.984 8.818m-.294 360.067c7.25 0 13.521-2.351 18.812-7.054 5.487-4.899 8.23-11.463 8.23-19.693v-238.967c0-8.23-2.646-14.696-7.936-19.4-5.095-4.703-11.17-7.054-18.224-7.054-7.25 0-13.619 2.352-19.106 7.054-5.291 4.703-7.936 11.17-7.936 19.4v238.967c0 8.622 2.547 15.284 7.642 19.987 5.095 4.507 11.267 6.76 18.518 6.76m141.382-.001c7.25 0 13.521-2.351 18.812-7.054 5.487-4.703 8.23-11.267 8.23-19.693v-128.154l138.148 146.966c4.899 5.487 10.777 8.23 17.636 8.23 6.466 0 12.149-2.449 17.048-7.348 5.095-4.899 7.642-10.484 7.642-16.754 0-5.683-2.156-10.777-6.466-15.284l-116.691-122.864 106.697-95.528c4.703-4.507 7.054-9.504 7.054-14.991 0-6.074-2.45-11.561-7.348-16.46-4.703-4.899-10.19-7.348-16.46-7.348-4.899 0-9.504 1.862-13.815 5.585l-133.445 122.276v-224.858c0-8.23-2.646-14.696-7.936-19.4-5.095-4.702-11.169-7.054-18.224-7.054-7.25 0-13.619 2.352-19.106 7.054-5.291 4.703-7.936 11.17-7.936 19.4v366.533c0 8.622 2.547 15.284 7.642 19.987 5.095 4.507 11.267 6.76 18.518 6.76"
|
||||
fill="#333"
|
||||
/>
|
||||
<path
|
||||
d="M776.626 2500.717c-33.865-2.92-59.743-13.135-78.565-31.014-19.15-18.191-27.367-41.907-24.124-69.629 3.732-31.901 18.891-54.429 45.47-67.572 20.485-10.129 40.921-13.983 74.202-13.99 23.646-.01 43.459 1.926 73.679 7.184l14.376 2.365c.632 0 1.133-2.378 2.505-11.887 2.481-17.203 2.925-23.14 2.249-30.031-.787-8.022-1.813-11.843-4.852-18.059-7.841-16.039-26.454-25.853-56.343-29.706-8.548-1.102-28.276-1.256-36.839-.288-14.347 1.622-22.352 3.689-40.207 10.379-10.446 3.914-10.494 3.926-14.66 3.682-7.163-.42-12.053-3.663-15.189-10.073-1.292-2.64-1.519-3.73-1.519-7.287 0-5.34 1.391-8.175 6.233-12.704 10.859-10.158 29.133-18.809 47.576-22.523 11.809-2.378 17.623-2.878 33.182-2.854 16.1.042 26.394.934 40.701 3.594 23.797 4.425 41.866 12.223 55.492 23.95 4.043 3.479 10.139 10.328 12.751 14.324.798 1.222 1.621 2.221 1.828 2.221s1.528-1.468 2.935-3.261c20.632-26.29 55.381-41.171 96.092-41.15 28.795 0 55.56 7.299 75.511 20.553 15.825 10.512 28.68 26.706 33.874 42.67 2.982 9.166 3.435 12.553 3.463 25.866.016 8.284-.212 13.871-.674 16.46-4.669 26.166-15.791 44.293-35.095 57.201-15.53 10.384-35.034 16.46-60.543 18.86-12.841 1.208-36.328 1.094-51.543-.251-12.913-1.141-28.769-3.149-35.533-4.499-5.662-1.13-25.051-4.368-25.21-4.209-.07.071-.993 5.869-2.05 12.888-1.688 11.207-1.927 14.003-1.962 22.951-.038 9.098.091 10.694 1.189 14.892 3.53 13.501 11.308 22.533 25.42 29.515 13.883 6.868 28.436 9.841 50.426 10.299 9.735.205 14.721.062 20.812-.583 14.765-1.566 21.493-3.283 41.11-10.495 4.886-1.796 9.872-3.431 11.081-3.632 2.59-.432 7.42.46 10.235 1.89 5.081 2.581 9.35 9.463 9.323 15.03-.029 6.504-2.223 10.444-8.663 15.567-15.397 12.25-35.056 19.763-58.523 22.365-8.368.928-27.474 1.065-36.702.263-25.989-2.257-44.033-6.702-60.523-14.909-12.867-6.403-22.683-14.347-30.015-24.289l-3.401-4.578c-.069-.075-1.266 1.336-2.661 3.135-6.166 7.954-15.595 16.503-24.809 22.494-13.302 8.649-32.399 15.247-51.722 17.872-5.763.782-24.882 1.428-29.785 1.005zm33.182-37.232c15.776-3.496 29.835-11.377 40.042-22.445 9.31-10.095 16.946-25.219 21.148-41.885 2.369-9.395 6.307-35.543 5.434-36.082-.344-.213-2.425-.55-4.624-.749s-8.465-.939-13.926-1.645c-32.435-4.195-52.334-5.825-63.489-5.2-21.208 1.188-29.854 3.039-42.587 9.121-6.523 3.115-11.026 6.496-14.588 10.953-5.111 6.395-8.196 12.75-10.346 21.313-1.565 6.231-1.724 22.547-.272 27.804 2.447 8.855 5.351 13.867 11.881 20.503 8.943 9.087 20.731 15.149 35.354 18.18 7.477 1.55 7.256 1.535 19.775 1.347 9.084-.138 12.483-.393 16.199-1.216h0zm206.667-122.602c13.194-1.164 21.307-3.213 31.275-7.896 8.29-3.895 13.109-7.876 17.669-14.598 6.713-9.896 9.327-19.261 9.26-33.176-.048-9.918-.889-14.043-4.312-21.163-7.65-15.908-25.62-27.303-49.189-31.19-5.881-.97-21.667-.798-27.695.301-11.597 2.116-21.4 5.973-30.046 11.821-15.974 10.804-26.998 27.09-33.136 48.954-2.411 8.588-3.054 11.884-5.204 26.687l-1.983 13.655 2.656.293 36.622 4.435c6.61.852 14.723 1.761 18.028 2.021 9.555.75 26.685.68 36.056-.146h0z"
|
||||
fill="#37abc8"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
</>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "User Permission Management",
|
||||
summary:
|
||||
"Detailed control over user permissions for accessing and managing projects and services.",
|
||||
description:
|
||||
"Allows administrators to define specific roles and permissions for each user, including the ability to create, modify, or delete applications and databases. This feature ensures secure and efficient management of large and diverse teams.",
|
||||
image: "/secondary/users.png",
|
||||
icon: function InventoryIcon() {
|
||||
return (
|
||||
<>
|
||||
<Users className="size-5 text-primary" />
|
||||
</>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Terminal Access",
|
||||
summary:
|
||||
"Direct access to each container's and server terminal for advanced management.",
|
||||
description:
|
||||
"Provides an interface to access the command line of any active container, allowing developers to execute commands, manage services, and troubleshoot directly from the dashboard",
|
||||
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;
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
className,
|
||||
!isActive ? "opacity-75 hover:opacity-100 " : "rounded-xl",
|
||||
" p-4 relative",
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
"size-9 rounded-lg flex items-center justify-center",
|
||||
isActive ? "bg-border" : "bg-muted",
|
||||
)}
|
||||
>
|
||||
<feature.icon />
|
||||
</div>
|
||||
{isActive && (
|
||||
<motion.span
|
||||
layoutId="bubble"
|
||||
className="absolute inset-0 z-10 bg-white/5 mix-blend-difference rounded-xl"
|
||||
transition={{
|
||||
type: "spring",
|
||||
bounce: 0.2,
|
||||
duration: 0.6,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<h3
|
||||
className={cn(
|
||||
"mt-6 text-sm font-medium",
|
||||
isActive ? "text-primary" : "text-primary/85",
|
||||
)}
|
||||
>
|
||||
{feature.name}
|
||||
</h3>
|
||||
<p className="mt-2 font-display text-xl text-foreground">
|
||||
{feature.summary}
|
||||
</p>
|
||||
<p className="mt-4 text-sm text-muted-foreground">
|
||||
{feature.description}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function FeaturesMobile() {
|
||||
return (
|
||||
<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 />
|
||||
<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">
|
||||
<img
|
||||
className="w-full"
|
||||
src={feature.image}
|
||||
alt=""
|
||||
sizes="52.75rem"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function FeaturesDesktop() {
|
||||
return (
|
||||
<Tab.Group as="div" className="hidden lg:mt-20 lg:block">
|
||||
{({ selectedIndex }) => (
|
||||
<>
|
||||
<Tab.List className="grid grid-cols-4 gap-x-8">
|
||||
{features.map((feature, featureIndex) => (
|
||||
<Feature
|
||||
key={feature.summary}
|
||||
feature={{
|
||||
...feature,
|
||||
name: (
|
||||
<Tab className="ui-not-focus-visible:outline-none">
|
||||
<span className="absolute inset-0" />
|
||||
{feature.name}
|
||||
</Tab>
|
||||
),
|
||||
}}
|
||||
isActive={featureIndex === selectedIndex}
|
||||
className="relative"
|
||||
/>
|
||||
))}
|
||||
</Tab.List>
|
||||
<Tab.Panels className="relative mt-20 overflow-hidden rounded-4xl bg-muted px-14 py-16 xl:px-16">
|
||||
<div className="-mx-5 flex">
|
||||
{features.map((feature, featureIndex) => (
|
||||
<Tab.Panel
|
||||
static
|
||||
key={feature.summary}
|
||||
className={cn(
|
||||
"px-5 transition duration-500 ease-in-out ui-not-focus-visible:outline-none",
|
||||
featureIndex !== selectedIndex && "opacity-60",
|
||||
)}
|
||||
style={{ transform: `translateX(-${selectedIndex * 100}%)` }}
|
||||
aria-hidden={featureIndex !== selectedIndex}
|
||||
>
|
||||
<div className="w-[52.75rem] overflow-hidden rounded-xl bg-red-500 shadow-lg shadow-slate-900/5 ring-1 ring-slate-500/10">
|
||||
<img
|
||||
className="w-full"
|
||||
src={feature.image}
|
||||
alt=""
|
||||
sizes="52.75rem"
|
||||
/>
|
||||
</div>
|
||||
</Tab.Panel>
|
||||
))}
|
||||
</div>
|
||||
<div className="pointer-events-none absolute inset-0 rounded-4xl ring-1 ring-inset ring-slate-900/10" />
|
||||
</Tab.Panels>
|
||||
</>
|
||||
)}
|
||||
</Tab.Group>
|
||||
);
|
||||
}
|
||||
|
||||
export function SecondaryFeatures() {
|
||||
return (
|
||||
<section
|
||||
id="secondary-features"
|
||||
aria-label="Features for simplifying everyday business tasks"
|
||||
className="pb-14 pt-20 sm:pb-20 sm:pt-32 lg:pb-32 bg-black"
|
||||
>
|
||||
<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">
|
||||
Advanced Management Tools
|
||||
</h2>
|
||||
<p className="mt-4 text-lg tracking-tight text-muted-foreground">
|
||||
Elevate your infrastructure with tools that offer precise control,
|
||||
detailed monitoring, and enhanced security, ensuring seamless
|
||||
management and robust performance.
|
||||
</p>
|
||||
</div>
|
||||
<FeaturesMobile />
|
||||
<FeaturesDesktop />
|
||||
</Container>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
31
apps/website/components/SlimLayout.tsx
Normal file
31
apps/website/components/SlimLayout.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import Link from "next/link";
|
||||
import { Footer } from "./Footer";
|
||||
import { Header } from "./Header";
|
||||
|
||||
export function SlimLayout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<Header />
|
||||
</div>
|
||||
<main className="text-center flex-auto items-center flex justify-center">
|
||||
<div>
|
||||
<h1 className="mb-4 text-6xl font-semibold text-primary">404</h1>
|
||||
<p className="mb-4 text-lg text-muted-foreground">
|
||||
Oops! Looks like you're lost.
|
||||
</p>
|
||||
<p className="mt-4 text-muted-foreground">
|
||||
Let's get you back{" "}
|
||||
<Link href="/" className="text-primary">
|
||||
home
|
||||
</Link>
|
||||
.
|
||||
</p>
|
||||
</div>
|
||||
</main>
|
||||
<div>
|
||||
<Footer />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
134
apps/website/components/Testimonials.tsx
Normal file
134
apps/website/components/Testimonials.tsx
Normal file
@@ -0,0 +1,134 @@
|
||||
import { Container } from "./Container";
|
||||
|
||||
const testimonials = [
|
||||
[
|
||||
{
|
||||
content:
|
||||
"This application has revolutionized the way we handle deployments. The integration of Docker and Traefik through such a user-friendly interface has saved us countless hours.",
|
||||
author: {
|
||||
name: "Emily R.",
|
||||
role: "Full Stack Developer",
|
||||
image: "/avatars/avatar-1.png",
|
||||
},
|
||||
},
|
||||
{
|
||||
content:
|
||||
"As a fast-paced startup, efficiency and reliability are paramount. This software delivered on both, allowing us to focus more on development and less on operations.",
|
||||
author: {
|
||||
name: "Mark T.",
|
||||
role: "CTO, Tech Innovations Inc.",
|
||||
image: "/avatars/avatar-2.png",
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
content:
|
||||
"The comprehensive monitoring and robust backup solutions have given us the peace of mind we need to operate at our best 24/7. Highly recommended!",
|
||||
author: {
|
||||
name: "Sarah L.",
|
||||
role: "IT Director, Creative Solutions Agency",
|
||||
image: "/avatars/avatar-3.png",
|
||||
},
|
||||
},
|
||||
{
|
||||
content:
|
||||
"Upgrading to this platform was a game-changer for our agency. The user permission controls and real-time updates have greatly enhanced our team's efficiency.",
|
||||
author: {
|
||||
name: "James P.",
|
||||
role: "Lead Developer, Dynamic Web Solutions",
|
||||
image: "/avatars/avatar-4.png",
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
content:
|
||||
"Fantastic tool! The direct container access and dynamic Traefik configuration features have made it so easy to manage our services. It's like having a DevOps team in a box!",
|
||||
author: {
|
||||
name: "Ana D.",
|
||||
role: "Full Stack Developer, Independent Contractor",
|
||||
image: "/avatars/avatar-7.png",
|
||||
},
|
||||
},
|
||||
{
|
||||
content:
|
||||
"his tool has been indispensable for managing my client projects. It has streamlined my workflow and dramatically increased my productivity, allowing me to take on more clients without sacrificing quality.",
|
||||
author: {
|
||||
name: "Carlos M.",
|
||||
role: "Freelance Full Stack Developer",
|
||||
image: "/avatars/avatar-6.png",
|
||||
},
|
||||
},
|
||||
],
|
||||
];
|
||||
|
||||
function QuoteIcon(props: React.ComponentPropsWithoutRef<"svg">) {
|
||||
return (
|
||||
<svg aria-hidden="true" width={105} height={78} {...props}>
|
||||
<path d="M25.086 77.292c-4.821 0-9.115-1.205-12.882-3.616-3.767-2.561-6.78-6.102-9.04-10.622C1.054 58.534 0 53.411 0 47.686c0-5.273.904-10.396 2.712-15.368 1.959-4.972 4.746-9.567 8.362-13.786a59.042 59.042 0 0 1 12.43-11.3C28.325 3.917 33.599 1.507 39.324 0l11.074 13.786c-6.479 2.561-11.677 5.951-15.594 10.17-3.767 4.219-5.65 7.835-5.65 10.848 0 1.356.377 2.863 1.13 4.52.904 1.507 2.637 3.089 5.198 4.746 3.767 2.41 6.328 4.972 7.684 7.684 1.507 2.561 2.26 5.5 2.26 8.814 0 5.123-1.959 9.19-5.876 12.204-3.767 3.013-8.588 4.52-14.464 4.52Zm54.24 0c-4.821 0-9.115-1.205-12.882-3.616-3.767-2.561-6.78-6.102-9.04-10.622-2.11-4.52-3.164-9.643-3.164-15.368 0-5.273.904-10.396 2.712-15.368 1.959-4.972 4.746-9.567 8.362-13.786a59.042 59.042 0 0 1 12.43-11.3C82.565 3.917 87.839 1.507 93.564 0l11.074 13.786c-6.479 2.561-11.677 5.951-15.594 10.17-3.767 4.219-5.65 7.835-5.65 10.848 0 1.356.377 2.863 1.13 4.52.904 1.507 2.637 3.089 5.198 4.746 3.767 2.41 6.328 4.972 7.684 7.684 1.507 2.561 2.26 5.5 2.26 8.814 0 5.123-1.959 9.19-5.876 12.204-3.767 3.013-8.588 4.52-14.464 4.52Z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function Testimonials() {
|
||||
return (
|
||||
<section
|
||||
id="testimonials"
|
||||
aria-label="What our customers are saying"
|
||||
className="bg-black py-20 sm:py-32"
|
||||
>
|
||||
<Container>
|
||||
<div className="mx-auto max-w-2xl md:text-center">
|
||||
<h2 className="font-display text-3xl tracking-tight sm:text-4xl">
|
||||
What Our Users Say
|
||||
</h2>
|
||||
<p className="mt-4 text-lg tracking-tight text-muted-foreground ">
|
||||
Don’t just take our word for it—see what our users across the globe
|
||||
are saying about how our platform has transformed their development
|
||||
workflows and boosted their productivity.
|
||||
</p>
|
||||
</div>
|
||||
<ul className="mx-auto mt-16 grid max-w-2xl grid-cols-1 gap-6 sm:gap-8 lg:mt-20 lg:max-w-none lg:grid-cols-3">
|
||||
{testimonials.map((column, columnIndex) => (
|
||||
<li key={columnIndex}>
|
||||
<ul className="flex flex-col gap-y-6 sm:gap-y-8">
|
||||
{column.map((testimonial, testimonialIndex) => (
|
||||
<li key={testimonialIndex}>
|
||||
<figure className="relative rounded-2xl bg-muted p-6 shadow-xl shadow-slate-900/10">
|
||||
<QuoteIcon className="absolute left-6 top-6 fill-border" />
|
||||
<blockquote className="relative">
|
||||
<p className="text-lg tracking-tight ">
|
||||
{testimonial.content}
|
||||
</p>
|
||||
</blockquote>
|
||||
<figcaption className="relative mt-6 flex items-center justify-between border-t border-border pt-6">
|
||||
<div>
|
||||
<div className="font-display text-base ">
|
||||
{testimonial.author.name}
|
||||
</div>
|
||||
<div className="mt-1 text-sm text-muted-foreground">
|
||||
{testimonial.author.role}
|
||||
</div>
|
||||
</div>
|
||||
<div className="overflow-hidden rounded-full bg-slate-50">
|
||||
<img
|
||||
className="h-14 w-14 object-cover"
|
||||
src={testimonial.author.image}
|
||||
alt=""
|
||||
width={56}
|
||||
height={56}
|
||||
/>
|
||||
</div>
|
||||
</figcaption>
|
||||
</figure>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</Container>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
17
apps/website/components/analitycs/google.tsx
Normal file
17
apps/website/components/analitycs/google.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect } from "react";
|
||||
import initializeGA from ".";
|
||||
|
||||
export default function GoogleAnalytics() {
|
||||
useEffect(() => {
|
||||
// @ts-ignore
|
||||
if (!window.GA_INITIALIZED) {
|
||||
initializeGA();
|
||||
// @ts-ignore
|
||||
window.GA_INITIALIZED = true;
|
||||
}
|
||||
}, []);
|
||||
|
||||
return null;
|
||||
}
|
||||
30
apps/website/components/analitycs/index.ts
Normal file
30
apps/website/components/analitycs/index.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
"use client";
|
||||
|
||||
import ReactGA from "react-ga4";
|
||||
|
||||
const initializeGA = () => {
|
||||
// Replace with your Measurement ID
|
||||
// It ideally comes from an environment variable
|
||||
ReactGA.initialize("G-0RTZ5EPB26");
|
||||
|
||||
// Don't forget to remove the console.log() statements
|
||||
// when you are done
|
||||
};
|
||||
|
||||
interface Props {
|
||||
category: string;
|
||||
action: string;
|
||||
label: string;
|
||||
}
|
||||
const trackGAEvent = ({ category, action, label }: Props) => {
|
||||
console.log("GA event:", category, ":", action, ":", label);
|
||||
// Send GA4 Event
|
||||
ReactGA.event({
|
||||
category: category,
|
||||
action: action,
|
||||
label: label,
|
||||
});
|
||||
};
|
||||
|
||||
export default initializeGA;
|
||||
export { initializeGA, trackGAEvent };
|
||||
25
apps/website/components/shared/Logo.tsx
Normal file
25
apps/website/components/shared/Logo.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
export function Logo(props: React.ComponentPropsWithoutRef<"svg">) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 559 446"
|
||||
className="h-10 w-10"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
className="fill-primary stroke-primary"
|
||||
d="M390 56v12c.1 2.3.5 4 1 6a73 73 0 0 0 12 24c2 2.3 5.7 4 7 7 4 3.4 9.6 6.8 14 9 1.7.6 5.7 1.1 7 2 1.9 1.3 2.9 2.3 0 4v1c-.6 1.8-1.9 3.5-3 5q-3 4-7 7c-4.3 3.2-9.5 6.8-15 7h-1q-2 1.6-5 2h-4c-5.2.7-12.9 2.2-18 0h-6c-1.6 0-3-.8-4-1h-3a17 17 0 0 1-6-2h-1c-2.5-.1-4-1.2-6-2l-4-1c-8.4-2-20.3-6.6-27-12h-1c-4.6-1-9.5-4.3-13.7-6.3s-10.5-3-13.3-6.7h-1c-4-1-8.9-3.5-12-6h-1c-6.8-1.6-13.6-6-20-9-6.5-2.8-14.6-5.7-20-10h-1c-7-1.2-15.4-4-22-6h-97c-5.3 4.3-13.7 4.3-18.7 10.3S90.8 101 88 108c-.4 1.5-.8 2.3-1 4-.2 1.6-.8 4-1 5v51c.2 1.2.8 3.2 1 5 .2 2 .5 3.2 1 5a79 79 0 0 0 6 12c.8.7 1.4 2.2 2 3 1.8 2 4.9 3.4 6 6 9.5 8.3 23.5 10.3 33 18h1c5.1 1.2 12 4.8 16 8h1c4 1 8.9 3.5 12 6h1q4.6 1.2 8 4h1c2 .1 2.6 1.3 4 2 1.6.8 2.7.7 4 2h1q2.5.3 4 2h1c3 .7 6.7 2 9 4h1c4.7.8 13.4 3.1 17 6h1c2.5.1 4 1.3 6 2 1.8.4 3 .8 5 1q3 .4 5 1c1.6-.2 2 0 3 1h1q2.5-.5 4 1h1q2.5-.5 4 1h1c2.2-.2 4.5-.3 6 1h1q4-.4 7 1h45c1.2-.2 3.1-1 5-1h6c1.5-.6 2.9-1.3 5-1h1q1.5-1.4 4-1h1q1.5-1.4 4-1h1c2.4-1.3 5-1.6 8-2l5-1c2-.7 3.6-1.6 6-2 4-.7 7.2-1.7 11-3 2.3-1 4.2-2.5 7-3h1q1.5-1.7 4-2h1c1.9-1.5 3.9-2 6-3q2.9-1.6 6-3a95 95 0 0 0 11-5c4.4-2.8 8.9-6 14-8 0 0 .6.2 1 0 1.8-2.8 7-4.8 10-6 0 0 .6.2 1 0 1.5-2.4 5.3-4 8-5 0 0 .6.2 1 0 1.5-2.4 5.3-4 8-5 0 0 .6.2 1 0 1.3-2 3.8-3.1 6-4 0 0 .6.2 1 0 2-3 7.7-5.6 11-7l5-2c6.3-3.8 11.8-9.6 18-14v-1c0-1.9-.4-4.2 0-6-1-4.5-3.9-5.5-7-8h-1c-1.2 0-2.8-.2-4 0-8.9 1.7-16.5 11.3-25.2 14.8-8.8 3.4-16.9 10.7-25.8 14.2h-1c-10.9 10.6-29.2 16-42.7 23.3S343.7 234.6 328 235h-1q-1.5 1.4-4 1h-1q-1.5 1.4-4 1h-1c-1.5 1.3-3.9 1.2-6 1h-1c-1.7 1.3-4.6 1.2-7 1-1 .2-2.4 1-4 1h-5c-6.6 0-13.4.4-20 0-1.9-.1-2.7.3-4-1h-8c-2.8-.2-5.7-1.3-8-2h-2q-5.7.4-10-2h-1q-4.5 0-8-2h-1a10 10 0 0 1-6-2h-1c-5.9-.2-12-3.8-17-6l-4-1c-1.7-.5-2.8-.7-4-2h-1q-2.5-.2-4-2h-1q-3.4-.9-6-3h-1c-3.5-.8-7.3-2.9-10-5h-1c-1.7 0-2.2-.7-3-2h-1c-11.6-2.7-23.2-11.5-34.2-15.8-11-4.2-25.9-9.2-29.8-21.2h4c16.2 0 32.8-1 49 0 1.7.1 3 .8 4 1 2.1.4 3.4-.5 5 1h1c3.6.1 8.4 1.8 11 4h1a45 45 0 0 1 18 8h1q4.6 1.2 8 4h1c4.2 1 8.3 3.4 12 5q3.4 1.2 7 2c5.7 1.3 13 2.3 18 5h1c3.7-.2 7 1.1 10 2h9c1.6 0 3 .8 4 1h32c2.2-1.6 6-1 9-1h1a63 63 0 0 1 22-4 22 22 0 0 1 8-2c1.7-1.4 3.7-1.6 6-2a81 81 0 0 0 12-3c2.3-1 4.2-2.5 7-3h1q1.5-1.7 4-2h1c1.9-1.5 3.6-2.2 6-3l3-1c4.1-2.3 8.4-5.2 13-7 0 0 .6.2 1 0 1.5-2.4 6.3-5 9-6 0 0 .6.2 1 0 5.3-8.1 17.6-12.5 24.8-20.2C439.9 144 445 133 452 126v-1a12 12 0 0 1 2-5c2.1-2.2 8.9-1 12-1q2 .2 4 0c1-.2 2.3-1.2 4-1h1q2.1-1.5 5-2h1q2.1-1.9 5-3s.6.2 1 0c9-9.3 18-15.4 23-28 1.1-2.8 3.5-6.4 4-9 .2-1 .2-3 0-4-1.5-6-12.3-2.4-15.7 2.3S484.7 80 479 80h-7c-7.8 4.3-19.3 5.7-23 16a37 37 0 0 0-22-24c-1.5-.5-2.5-.7-4-1-2.1-.5-3.6-.2-5-2h-1a22 22 0 0 1-12-8c-2-2.9-3.4-6.5-6-9h-1c-3.9-.6-6.1 1-8 4m-181 45h1c2.2-.2 4.5-.3 6 1h1q2.5-.5 4 1h1a33 33 0 0 1 17 7h1c4.4 1 8.2 4.1 12 6 2.1 1 4.1 1.5 6 3h1c4 1 8.9 3.5 12 6h1c4 1 8.9 3.5 12 6h1c4 1 8.9 3.5 12 6h1a61 61 0 0 1 21 10h1c3.5.8 7.3 2.9 10 5h1c6.1 1.4 12.3 5 18 7 1.8.4 3 .8 5 1 1.8.2 3.7.8 5 1q2.5-.5 4 1h6c2.5 0 4 .3 6 1h3q-.7 2.1-3 2a46 46 0 0 1-16 7l-10 3c-2 .8-3.4 1.9-6 2h-1c-2.6 2.1-7.5 3-11 3h-1c-3.1 2.5-10.7 3.5-15 3h-1c-1.5 1.3-3.9 1.2-6 1-1 .2-2.4 1-4 1h-11c-3.8.4-8.3.4-12 0h-9c-2.3 0-4.3-.7-6-1h-3c-1.8 0-2.9-.7-4-1-3.5-.8-7-.7-10-2h-1c-4.1-.7-9.8-1.4-13-4h-1q-4-.6-7-3h-1q-2.5-.2-4-2h-1q-3.4-.9-6-3h-1c-7.2-1.7-13.3-5.9-20.2-8.8-7-2.8-16.2-4.3-22.8-7.2h-11c-14 0-28.9.3-42-1-2.3 0-4.8.3-7 0a6 6 0 0 1-5-5c-1.8-4.8-.4-10.4 0-15 0-4.3-.4-8.7 0-13 .2-3.2 2.2-7.3 4-10q2-3 5-5c2.1-2 5.4-2.3 8-3 15.6-3.9 36.3-1 53-1 5.2 0 12-.5 17 0s12.2-1.8 16 1Z"
|
||||
/>
|
||||
|
||||
<path
|
||||
className="fill-primary stroke-primary"
|
||||
d="M162 132v1c1.8 2.9 4.5 5.3 8 6 .3-.2 3.7-.2 4 0 7-1.4 9.2-8.8 7-15v-1a14 14 0 0 0-7-4c-.3.2-3.7.2-4 0-6.5 1.3-8.6 6.8-8 13Z"
|
||||
/>
|
||||
|
||||
<path
|
||||
className="fill-primary stroke-primary"
|
||||
d="M465 211h-1c-18.2 14.6-41.2 24.6-60 39-19 14.2-42.7 29.3-66 34l-4 1c-2.4 1-4 2-7 2h-1q-3.5 2-8 2h-1c-1.3 1.2-3 1.1-5 1h-2q-2.6 1.1-6 1h-2c-3 1.2-6.5 1-10 1-6.3.6-13.8.6-20 0-3.4 0-8.4.9-11-1h-1c-2.2.2-4.5.3-6-1h-1c-2 .2-3.7.2-5-1h-1c-7.6.5-16.5-3.4-23-6l-4-1a129 129 0 0 1-36.2-15.8c-10.4-6.6-23.2-12.8-32.5-20.5-9.2-7.7-23.8-12.8-30.3-22.7h-1c-2.3-1.4-4.5-2.7-6-5h-1c-4-2.5-8.5-5.2-12-8h-9a9 9 0 0 0-6 7c.3 3.3 0 6.7 0 10v9c.2 1.6 1 3.8 1 6v3c.2 1 1.2 2.2 1 4v1c1.2 1.2.8 2.2 1 4 .8 6.7 3 12.6 5 19 1.7 4.3 4.2 9.1 5 14v1q1.8 1.5 2 4v1a36 36 0 0 1 5 10c.7 2 1 3 2 5 8 12.7 15.7 25.5 25.8 37.3 10 11.7 20.8 20.6 32.4 30.4 11.7 9.9 28.3 14 39.8 23.3h1q2.5.3 4 2h1c2.8.4 4.8 2 7 3l7 2c5.7 1.3 13 2.3 18 5h1c2.1-.3 3.6.8 5 1h3c2.8.2 5.8 1 8 2h8c2.1 0 4.6.8 6 1h21c1.2-.2 3.2-1 5-1h9c3.3-1 7-2.4 11-2h1c2.7-2.2 7.4-2.4 11-3a55 55 0 0 0 8-2c6.5-2.6 13.9-6.3 21-8h1c8.5-6.8 20.6-9.7 29.2-16.8 8.7-7 18.3-12.8 26.8-20.2 4.4-3.8 9-9 13-13 14.8-14.8 20.7-34.6 33-50v-1q.9-3.4 3-6v-1q.3-2.5 2-4v-1c.5-3.3 2-8.6 4-11v-1q0-3.5 2-6v-1c1.1-6.7 2.4-15 5-21v-1c-.2-2-.2-3.7 1-5v-8c0-5.3-.5-10.8 0-16a14 14 0 0 0-4-6c-1-.5-1.1-.4-2-1h-6q-2.1 1.5-5 2m-6 38c-2.1 13.4-21.2 20.3-31 30-10 9.5-23.7 19-35 27-11.5 8-25.1 19.7-39 23h-1a22 22 0 0 1-10 4h-1a25 25 0 0 1-12 4h-1q-3.5 2-8 2h-1c-1.1 1.1-2.3 1-4 1h-2c-1.2.4-2.2 1-4 1h-2c-1.8.7-3.6 1.3-6 1h-1c-1.2 1.2-2.3 1-4 1h-5c-5.7.6-12.3.8-18 0h-4c-1.9 0-2.7-.6-4-1h-6c-1.9 0-2.7.3-4-1h-1q-2.5.5-4-1h-1c-8.1.5-16.8-3.6-24.2-5.8S210 329.8 204 325h-1c-12.8-5-27.1-15.6-37.7-24.3S138.8 284.2 131 273c-.3-.2-1 0-1 0-5.7-4.4-16.6-10-19-17-.9-2.6-1-5.4-2-8-.8-2.2-2.5-5-2-8a667 667 0 0 0 88 56h1q3.4.9 6 3h1c2.8.4 4.8 2 7 3q5 1.8 10 3l6 2q2.9.6 6 1 3 .4 5 1c1.6-.2 2 0 3 1h1c2-.2 3.7-.2 5 1h1c2.2-.3 3.4.4 5 1h8c1.6 0 3 .9 4 1h40c1.8-1.3 4.6-1.2 7-1h1c1.2-1.2 3.2-1.2 5-1h1c1.2-1.2 3.2-1.2 5-1h1c1.1-1.1 2.3-1 4-1h2c3.5-1.7 6.9-2.3 11-3l4-1c3.4-1.4 7.1-3 11-4 1.5-.4 2.5-.5 4-1 1.4-.7 2-1.9 4-2h1q2.6-2.1 6-3h1c2.5-2 6-3.8 9-5l3-1c1.4-.9 2-2.5 4-3h1q1.4-2.2 4-3h1c7.3-7.7 19-13.2 27.7-19.3 8.8-6.1 18.2-15 28.3-18.7.4-.2 1 0 1 0q3.8-3.9 9-6c1.3 2.5-.5 6.7-1 10m-20 55c-.2.4 0 1 0 1-3.4 9.6-12.7 19-19 27a88 88 0 0 1-12 12 214 214 0 0 1-26.7 20.3c-9.5 5.8-20 14.8-31.3 16.7h-1a22 22 0 0 1-10 4h-1c-3.2 2.6-8.9 3.3-13 4h-1q-1.5 1.4-4 1h-1q-1.5 1.4-4 1h-1c-4.9 2.3-10.5 1-16 2-1 .2-2.5 1-4 1-6.2.4-12.8.3-19 0-1.8 0-3.8-.8-5-1h-4c-1.6 0-3-.9-4-1h-4c-3.9-.3-8.8-1.3-12-3h-1c-3.3-.5-7.5-1-10-3h-1c-3.6-.1-8.4-1.8-11-4h-1c-3.9-.6-8-2.6-11-5h-1c-16.1-3.8-32.2-18.9-45-29a200 200 0 0 1-40-51c17.7 11.5 35 25.5 52 38h1c4 1.6 12.8 5.4 15 9h1c4.6 1 10.4 4.1 14 7h1q2.5.3 4 2h1c3.3.5 8.6 2 11 4h1q3.5 0 6 2h1q2.5-.5 4 1h1q2.5-.5 4 1h1c3.8-.2 7.9 1 11 2h9c1.6 0 3 .8 4 1h32c1.2-.2 3.2-1 5-1h8a139 139 0 0 1 20-4l5-1c2-.7 3.7-1.5 6-2l4-1c1.5-.6 3-1.7 5-2h1q3-2.4 7-3h1q2.6-2.1 6-3h1c11.7-9.4 27.6-14.6 39-25 11.6-10.3 25-18.5 37-28a15 15 0 0 1-5 10Z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
60
apps/website/components/ui/button.tsx
Normal file
60
apps/website/components/ui/button.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
"use client";
|
||||
|
||||
import { Slot } from "@radix-ui/react-slot";
|
||||
import { type VariantProps, cva } from "class-variance-authority";
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "../../lib/utils";
|
||||
|
||||
const buttonVariants = cva(
|
||||
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm will-change-transform transition-all active:hover:scale-[0.98] font-medium ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
||||
destructive:
|
||||
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
||||
outline:
|
||||
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
||||
secondary:
|
||||
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||
ghost: "hover:bg-accent hover:text-accent-foreground",
|
||||
link: "text-primary underline-offset-4 hover:underline",
|
||||
},
|
||||
size: {
|
||||
default: "h-10 px-4 py-2",
|
||||
sm: "h-9 rounded-md px-3",
|
||||
lg: "h-11 rounded-md px-8",
|
||||
icon: "h-10 w-10",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: "default",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
export interface ButtonProps
|
||||
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||
VariantProps<typeof buttonVariants> {
|
||||
asChild?: boolean;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
({ className, variant, size, asChild = false, ...props }, ref) => {
|
||||
const Comp = asChild ? Slot : "button";
|
||||
return (
|
||||
// @ts-expect-error
|
||||
<Comp
|
||||
className={cn(buttonVariants({ variant, size, className }))}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
},
|
||||
);
|
||||
Button.displayName = "Button";
|
||||
|
||||
export { Button, buttonVariants };
|
||||
Reference in New Issue
Block a user