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:
Mauricio Siu
2024-07-29 23:08:23 -06:00
committed by GitHub
parent 9eba4f8b84
commit 5280c861e8
942 changed files with 87366 additions and 6709 deletions

File diff suppressed because one or more lines are too long

View 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}
/>
);
}

View 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 cant find what youre looking for, email our support team
and if youre 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>
);
}

View 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 &copy; {new Date().getFullYear()} Dokploy. All rights
reserved.
</p>
</div>
</Container>
</footer>
);
}

View 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>
);
}

File diff suppressed because one or more lines are too long

View 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>
);
}

View 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 backupsall 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>
);
}

View 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>
);
}

View 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>
</>
);
}

View 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 ">
Dont just take our word for itsee 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>
);
}

View 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;
}

View 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 };

View 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>
);
}

View 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 };