feat(website): add new landing page

This commit is contained in:
Mauricio Siu
2024-11-01 03:04:13 -06:00
parent ae4133a819
commit 0f1f1a1935
26 changed files with 1496 additions and 315 deletions

View File

@@ -2,18 +2,19 @@ import { CallToAction } from "@/components/CallToAction";
import { Faqs } from "@/components/Faqs";
import { Hero } from "@/components/Hero";
import { PrimaryFeatures } from "@/components/PrimaryFeatures";
import { SecondaryFeatures } from "@/components/SecondaryFeatures";
import { Testimonials } from "@/components/Testimonials";
import { FeaturesSectionDemo } from "@/components/features";
export default function Home() {
return (
<div>
<main>
<Hero />
<FeaturesSectionDemo />
<PrimaryFeatures />
<SecondaryFeatures />
<CallToAction />
{/* <Testimonials /> */}
<Testimonials />
<Faqs />
<CallToAction />
</main>
</div>
);

View File

@@ -41,7 +41,7 @@ export function CallToAction() {
<Button className="mt-10 rounded-full" asChild>
<Link
href={linkT("docs.install")}
href={"https://app.dokploy.com/register"}
aria-label="Dokploy on GitHub"
target="_blank"
className="flex flex-row items-center gap-2"

View File

@@ -1,53 +1,49 @@
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
import { useTranslations } from "next-intl";
import { Container } from "./Container";
const faqs = [
[
{
question: "faq.q1",
answer: "faq.a1",
},
{
question: "faq.q2",
answer: "faq.a2",
},
{
question: "faq.q3",
answer: "faq.a3",
},
{
question: "faq.q4",
answer: "faq.a4",
},
],
[
{
question: "faq.q5",
answer: "faq.a5",
},
{
question: "faq.q6",
answer: "faq.a6",
},
{
question: "faq.q7",
answer: "faq.a7",
},
],
[
{
question: "faq.q8",
answer: "faq.a8",
},
{
question: "faq.q9",
answer: "faq.a9",
},
{
question: "faq.q10",
answer: "faq.a10",
},
],
{
question: "faq.q1",
answer: "faq.a1",
},
{
question: "faq.q2",
answer: "faq.a2",
},
{
question: "faq.q4",
answer: "faq.a4",
},
{
question: "faq.q5",
answer: "faq.a5",
},
{
question: "faq.q6",
answer: "faq.a6",
},
{
question: "faq.q7",
answer: "faq.a7",
},
{
question: "faq.q8",
answer: "faq.a8",
},
{
question: "faq.q9",
answer: "faq.a9",
},
{
question: "faq.q10",
answer: "faq.a10",
},
];
export function Faqs() {
@@ -58,36 +54,31 @@ export function 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">
<Container className="relative flex flex-col gap-10">
<div className="mx-auto lg:mx-0 justify-center w-full">
<h2
id="faq-title"
className="font-display text-3xl tracking-tight text-primary sm:text-4xl"
className="font-display text-3xl tracking-tight text-primary sm:text-4xl text-center"
>
{t("faq.title")}
</h2>
<p className="mt-4 text-lg tracking-tight text-muted-foreground">
<p className="mt-4 text-lg tracking-tight text-muted-foreground text-center">
{t("faq.des")}
</p>
</div>
<ul className="mx-auto mt-16 grid max-w-2xl grid-cols-1 gap-8 lg:max-w-none lg:grid-cols-3">
<Accordion
type="single"
collapsible
className="w-full max-w-3xl mx-auto"
>
{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">
{t(faq.question)}
</h3>
<p className="mt-4 text-sm text-muted-foreground">
{t(faq.answer)}
</p>
</li>
))}
</ul>
</li>
<AccordionItem value={`${columnIndex}`} key={columnIndex}>
<AccordionTrigger>{t(column.question)}</AccordionTrigger>
<AccordionContent>{t(column.answer)}</AccordionContent>
</AccordionItem>
))}
</ul>
</Accordion>
</Container>
</section>
);

View File

@@ -165,12 +165,11 @@ export function Header() {
</span>
<HeartIcon className="animate-heartbeat size-4 fill-red-600 text-red-500 " />
</Link>
<Button className="rounded-xl" asChild>
<Button className="rounded-full" asChild>
<Link
href="https://app.dokploy.com"
aria-label="Dokploy on GitHub"
target="_blank"
// className="flex flex-row items-center gap-2 text-white"
>
{t("navigation.dashboard")}
</Link>

View File

@@ -1,12 +1,22 @@
"use client";
import { cn } from "@/lib/utils";
import { ArrowRight, ArrowRightIcon, Check, Copy } from "lucide-react";
import { motion } from "framer-motion";
import {
ArrowRight,
ArrowRightIcon,
Check,
ChevronRight,
Copy,
} from "lucide-react";
import { useTranslations } from "next-intl";
import Link from "next/link";
import { useEffect, useState } from "react";
import { Container } from "./Container";
import Hero12 from "./hero/Hi";
import AnimatedGradientText from "./ui/animated-gradient-text";
import AnimatedShinyText from "./ui/animated-shiny-text";
import { Button } from "./ui/button";
import { Button, buttonVariants } from "./ui/button";
import HeroVideoDialog from "./ui/hero-video-dialog";
import { HoverBorderGradient } from "./ui/hover-border-gradient";
const ProductHunt = () => {
@@ -72,151 +82,141 @@ export function Hero() {
</div>
<div className="relative">
<Link href="/pricing" className="relative z-10 mb-4 inline-block">
<div className="flex items-center justify-center">
<div
className={cn(
"group rounded-full border border-black/5 bg-neutral-100 text-sm font-medium text-white transition-all ease-in hover:cursor-pointer hover:bg-neutral-200 dark:border-white/5 dark:bg-neutral-900 dark:hover:bg-neutral-800",
)}
>
<AnimatedShinyText className="inline-flex items-center justify-center px-4 py-1 text-neutral-800 transition ease-out hover:text-neutral-900 hover:duration-300 hover:dark:text-neutral-400">
<span>🚀 {t("hero.cloud")} </span>
<ArrowRightIcon className="ml-1 size-3 transition-transform duration-300 ease-in-out group-hover:translate-x-0.5" />
</AnimatedShinyText>
<div className="text-center">
<motion.a
href="/pricing"
className="relative z-10 mb-4 inline-block"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3 }}
>
<div className="z-10 flex items-center justify-center">
<AnimatedGradientText>
🎉 <hr className="mx-2 h-4 w-px shrink-0 bg-gray-300" />{" "}
<span
className={cn(
"inline animate-gradient bg-gradient-to-r from-[#ffaa40] via-[#9c40ff] to-[#ffaa40] bg-[length:var(--bg-size)_100%] bg-clip-text text-transparent",
)}
>
Introducing Dokploy Cloud
</span>
<ChevronRight className="ml-1 size-3 transition-transform duration-300 ease-in-out group-hover:translate-x-0.5" />
</AnimatedGradientText>
</div>
</div>
</Link>
</motion.a>
<h1 className="mx-auto max-w-4xl font-display text-5xl font-medium tracking-tight text-muted-foreground sm:text-7xl">
{t("hero.deploy")}{" "}
<span className="relative whitespace-nowrap text-primary">
<svg
aria-hidden="true"
viewBox="0 0 418 42"
className="absolute left-0 top-2/3 h-[0.58em] w-full fill-primary"
preserveAspectRatio="none"
>
<path d="M203.371.916c-26.013-2.078-76.686 1.963-124.73 9.946L67.3 12.749C35.421 18.062 18.2 21.766 6.004 25.934 1.244 27.561.828 27.778.874 28.61c.07 1.214.828 1.121 9.595-1.176 9.072-2.377 17.15-3.92 39.246-7.496C123.565 7.986 157.869 4.492 195.942 5.046c7.461.108 19.25 1.696 19.17 2.582-.107 1.183-7.874 4.31-25.75 10.366-21.992 7.45-35.43 12.534-36.701 13.884-2.173 2.308-.202 4.407 4.442 4.734 2.654.187 3.263.157 15.593-.78 35.401-2.686 57.944-3.488 88.365-3.143 46.327.526 75.721 2.23 130.788 7.584 19.787 1.924 20.814 1.98 24.557 1.332l.066-.011c1.201-.203 1.53-1.825.399-2.335-2.911-1.31-4.893-1.604-22.048-3.261-57.509-5.556-87.871-7.36-132.059-7.842-23.239-.254-33.617-.116-50.627.674-11.629.54-42.371 2.494-46.696 2.967-2.359.259 8.133-3.625 26.504-9.81 23.239-7.825 27.934-10.149 28.304-14.005.417-4.348-3.529-6-16.878-7.066Z" />
</svg>
<span className="relative"> {t("hero.anywhere")}</span>
</span>{" "}
{t("hero.with")}
</h1>
<p className="mx-auto mt-6 max-w-2xl text-lg tracking-tight text-muted-foreground">
{t("hero.des")}
</p>
<div className="flex flex-col gap-6 md:gap-10">
<div className="mt-10 flex flex-wrap items-center justify-center gap-6 md:flex-nowrap">
<code className="flex flex-row items-center gap-4 rounded-xl border p-3 font-sans">
curl -sSL https://dokploy.com/install.sh | sh
<button
type="button"
onClick={() =>
navigator.clipboard
.writeText("curl -sSL https://dokploy.com/install.sh | sh")
.then(() => setIsCopied(true))
.catch(() => setIsCopied(false))
}
<motion.h1
className="mx-auto max-w-4xl font-display text-5xl font-medium tracking-tight text-muted-foreground sm:text-7xl"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3 }}
>
{t("hero.deploy")}{" "}
<span className="relative whitespace-nowrap text-primary">
<svg
aria-hidden="true"
viewBox="0 0 418 42"
className="absolute left-0 top-2/3 h-[0.58em] w-full fill-primary"
preserveAspectRatio="none"
>
{isCopied ? (
<Check className="h-4 w-4 text-muted-foreground" />
) : (
<Copy className="h-4 w-4 text-muted-foreground" />
)}
</button>
</code>
{/* <Button className="rounded-xl" asChild>
<Link
href="https://discord.gg/2tBnJ3jDJc"
aria-label="Dokploy on GitHub"
target="_blank"
className="flex flex-row gap-2 items-center"
>
<svg
role="img"
className="h-6 w-6 fill-[#5867F6]"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
<path d="M203.371.916c-26.013-2.078-76.686 1.963-124.73 9.946L67.3 12.749C35.421 18.062 18.2 21.766 6.004 25.934 1.244 27.561.828 27.778.874 28.61c.07 1.214.828 1.121 9.595-1.176 9.072-2.377 17.15-3.92 39.246-7.496C123.565 7.986 157.869 4.492 195.942 5.046c7.461.108 19.25 1.696 19.17 2.582-.107 1.183-7.874 4.31-25.75 10.366-21.992 7.45-35.43 12.534-36.701 13.884-2.173 2.308-.202 4.407 4.442 4.734 2.654.187 3.263.157 15.593-.78 35.401-2.686 57.944-3.488 88.365-3.143 46.327.526 75.721 2.23 130.788 7.584 19.787 1.924 20.814 1.98 24.557 1.332l.066-.011c1.201-.203 1.53-1.825.399-2.335-2.911-1.31-4.893-1.604-22.048-3.261-57.509-5.556-87.871-7.36-132.059-7.842-23.239-.254-33.617-.116-50.627.674-11.629.54-42.371 2.494-46.696 2.967-2.359.259 8.133-3.625 26.504-9.81 23.239-7.825 27.934-10.149 28.304-14.005.417-4.348-3.529-6-16.878-7.066Z" />
</svg>
<span className="relative"> {t("hero.anywhere")}</span>
</span>{" "}
{t("hero.with")}
</motion.h1>
<motion.p
className="mx-auto mt-6 max-w-2xl text-lg tracking-tight text-muted-foreground"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3, delay: 0.2 }}
>
{t("hero.des")}
</motion.p>
<motion.div
className="flex flex-col sm:flex-row justify-center items-center space-y-4 sm:space-y-0 sm:space-x-4"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3, delay: 0.4 }}
>
<div className="flex flex-col gap-6">
<div className="mt-6 flex flex-wrap items-center justify-center gap-6 md:flex-nowrap">
<code className="flex flex-row items-center gap-4 rounded-xl border p-3 font-sans">
curl -sSL https://dokploy.com/install.sh | sh
<button
type="button"
onClick={() =>
navigator.clipboard
.writeText(
"curl -sSL https://dokploy.com/install.sh | sh",
)
.then(() => setIsCopied(true))
.catch(() => setIsCopied(false))
}
>
{isCopied ? (
<Check className="h-4 w-4 text-muted-foreground" />
) : (
<Copy className="h-4 w-4 text-muted-foreground" />
)}
</button>
</code>
</div>
<div className="mx-auto flex w-full max-w-sm flex-wrap items-center justify-center gap-3 md:flex-nowrap">
<Button className="w-full rounded-full" asChild>
<Link
href="https://github.com/dokploy/dokploy"
aria-label="Dokploy on GitHub"
target="_blank"
className="flex flex-row items-center gap-2"
>
<svg aria-hidden="true" className="h-6 w-6 fill-black">
<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>
Github
</Link>
</Button>
<Button
className="w-full rounded-full bg-[#5965F2] hover:bg-[#4A55E0]"
asChild
>
<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"></path>
</svg>
Discord
</Link>
</Button> */}
</div>
<div className="mx-auto flex w-full max-w-sm flex-wrap items-center justify-center gap-3 md:flex-nowrap">
<Button className="w-full rounded-xl" asChild>
<Link
href="https://github.com/dokploy/dokploy"
aria-label="Dokploy on GitHub"
target="_blank"
className="flex flex-row items-center gap-2"
>
<svg aria-hidden="true" className="h-6 w-6 fill-black">
<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>
Github
</Link>
</Button>
<Button
className="w-full rounded-xl bg-[#5965F2] hover:bg-[#4A55E0]"
asChild
>
<Link
href="https://discord.gg/2tBnJ3jDJc"
aria-label="Dokploy on GitHub"
target="_blank"
className="flex flex-row items-center gap-2 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>
{t("navigation.discord")}
</Link>
</Button>
</div>
<Link
href="https://discord.gg/2tBnJ3jDJc"
aria-label="Dokploy on GitHub"
target="_blank"
className="flex flex-row items-center gap-2 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>
{t("navigation.discord")}
</Link>
</Button>
</div>
</div>
</motion.div>
</div>
<div className="mt-16 flex flex-row justify-center gap-x-8 rounded-lg sm:gap-x-0 sm:gap-y-10 xl:gap-x-12 xl:gap-y-0">
<iframe
width="460"
height="215"
src="https://www.youtube-nocookie.com/embed/mznYKPvhcfw?si=vHvqP3HKy0V3XkOZ"
title="YouTube video player"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerPolicy="strict-origin-when-cross-origin"
allowFullScreen
className="rounded-xl"
/>
</div>
<ShowSponsors />
{/* <div className="mt-16">
<p className="font-display text-base text-primary">
{t("hero.featuredIn")}
</p>
<ul className="mt-8 flex items-center justify-center gap-x-8 sm:flex-col sm:gap-x-0 sm:gap-y-10 xl:flex-row xl:gap-x-12 xl:gap-y-0">
{[
[
{ name: "Product Hunt", logo: <ProductHunt /> },
// { name: "Hacker News", logo: <ProductHunt /> },
],
].map((group, groupIndex) => (
<li key={`group-${groupIndex}`}>
<ul className="flex flex-col items-center gap-y-8 sm:flex-row sm:gap-x-12 sm:gap-y-0">
{group.map((company) => (
<li key={company.name} className="flex">
{company.logo}
</li>
))}
</ul>
</li>
))}
</ul>
</div> */}
<motion.div
className="mt-10 max-w-2xl mx-auto"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3, delay: 0.6 }}
>
<div className="mt-10 flex flex-row justify-center gap-x-8 rounded-lg sm:gap-x-0 sm:gap-y-10 xl:gap-x-12 xl:gap-y-0">
<HeroVideoDialog
className="block max-w-md w-full rounded-xl"
animationStyle="top-in-bottom-out"
videoSrc="https://www.youtube-nocookie.com/embed/mznYKPvhcfw?si=vHvqP3HKy0V3XkOZ"
thumbnailSrc="https://dokploy.com/banner.webp"
thumbnailAlt="Hero Video"
/>
</div>
</motion.div>
</div>
</Container>
);

View File

@@ -7,17 +7,13 @@ import { useEffect, useState } from "react";
import { cn } from "@/lib/utils";
import { useTranslations } from "next-intl";
import { Container } from "./Container";
import Safari from "./ui/safari";
const features = [
{
title: "primaryFeatures.projects",
description: "primaryFeatures.projectsDes",
image: "/primary/projects.png",
},
{
title: "primaryFeatures.applications",
description: "primaryFeatures.applicationsDes",
image: "/primary/applications.png",
image: "/primary/primary.png",
},
{
title: "primaryFeatures.compose",
@@ -25,9 +21,9 @@ const features = [
image: "/primary/compose.png",
},
{
title: "primaryFeatures.multinode",
description: "primaryFeatures.multinodeDes",
image: "/primary/multinode.png",
title: "primaryFeatures.multiserver",
description: "primaryFeatures.multiserverDes",
image: "/primary/servers.png",
},
{
title: "primaryFeatures.monitoring",
@@ -39,6 +35,11 @@ const features = [
description: "primaryFeatures.backupsDes",
image: "/primary/backups.png",
},
{
title: "primaryFeatures.traefik",
description: "primaryFeatures.traefikDes",
image: "/primary/traefik.png",
},
];
export function PrimaryFeatures() {
@@ -85,7 +86,7 @@ export function PrimaryFeatures() {
height={1636}
unoptimized
/> */}
<Container className="relative">
<div className="mx-auto max-w-7xl max-lg:px-4 relative">
<div className="max-w-2xl md:mx-auto md:text-center xl:max-w-none">
<h2 className="font-display text-3xl tracking-tight text-white sm:text-4xl md:text-5xl">
{t("primaryFeatures.title")}
@@ -96,16 +97,16 @@ export function PrimaryFeatures() {
</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"}
className="mt-16 grid grid-cols-1 items-center gap-y-2 pt-10 sm:gap-y-6 md:mt-20"
vertical={false}
>
{({ selectedIndex }) => (
<>
<div className="-mx-4 flex overflow-x-auto pb-4 sm:mx-0 sm:overflow-visible sm:pb-0 lg:col-span-5">
<div className="-mx-4 flex overflow-x-auto pb-4 sm:mx-0 overflow-visible sm:pb-0">
<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"
className="relative z-10 flex gap-x-4 whitespace-nowrap px-4 sm:mx-auto sm:px-0 "
>
{features.map((feature, featureIndex) => (
<motion.div
@@ -113,14 +114,14 @@ export function PrimaryFeatures() {
initial={false}
key={`feature-${featureIndex}`}
className={cn(
"group relative rounded-full px-4 py-1 transition-colors lg:rounded-l-xl lg:rounded-r-none lg:p-6 ",
"group relative rounded-full px-4 py-1 transition-colors ",
)}
>
<AnimatePresence>
{selectedIndex === featureIndex && (
<motion.span
layoutId="tab"
className="absolute inset-0 z-10 rounded-full bg-white/5 mix-blend-difference lg:rounded-l-xl lg:rounded-r-none"
className="absolute inset-0 z-10 rounded-full bg-white/5 mix-blend-difference"
initial={{ opacity: 1 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
@@ -138,13 +139,13 @@ export function PrimaryFeatures() {
"font-display text-lg text-primary ui-not-focus-visible:outline-none",
)}
>
<span className="absolute inset-0 rounded-full lg:rounded-l-xl lg:rounded-r-none" />
<span className="absolute inset-0 rounded-full" />
{t(feature.title)}
</Tab>
</h3>
<p
className={cn(
"mt-2 hidden text-sm text-muted-foreground lg:block",
"mt-2 hidden text-sm text-muted-foreground ",
)}
>
{t(feature.description)}
@@ -153,34 +154,35 @@ export function PrimaryFeatures() {
))}
</Tab.List>
</div>
<Tab.Panels className="lg:col-span-7">
<Tab.Panels className="">
{features.map((feature, index) => (
<Tab.Panel key={`panel-${index}`}>
<div className="relative sm:px-6 lg:hidden">
<div className="relative sm:px-6 ">
<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">
<p className="relative mx-auto max-w-2xl text-base text-white sm:text-center mb-10">
{t(feature.description)}
</p>
</div>
<motion.div
key={feature.title}
initial={isMounted ? { opacity: 0.8, x: 50 } : {}}
animate={isMounted ? { opacity: 1, x: 0 } : {}}
initial={isMounted ? { opacity: 0.4 } : {}}
animate={isMounted ? { opacity: 1 } : {}}
exit={{ opacity: 0, x: -50 }}
transition={{
type: "spring",
bounce: 0.2,
duration: 0.6,
duration: 0.8,
}}
className="mt-10 h-[24rem] w-[45rem] overflow-hidden rounded-xl border shadow-xl sm:w-auto lg:mt-0 lg:h-[40rem] lg:w-[67.8125rem]"
className="mt-10 h-[24rem] w-[45rem] overflow-hidden rounded-xl border shadow-xl sm:w-auto lg:mt-0 lg:h-[40rem] "
>
<img
alt=""
className="w-full"
src={feature.image}
srcSet={`${feature.image} 1x`}
/>
<div className="relative">
<Safari
url={"Dokploy UI"}
className="size-full"
src={feature.image}
/>
</div>
</motion.div>
</Tab.Panel>
))}
@@ -188,7 +190,7 @@ export function PrimaryFeatures() {
</>
)}
</Tab.Group>
</Container>
</div>
</section>
);
}

View File

@@ -1,4 +1,7 @@
"use client";
import { cn } from "@/lib/utils";
import { Container } from "./Container";
import { Marquee } from "./ui/marquee";
const testimonials = [
[
@@ -71,64 +74,144 @@ function QuoteIcon(props: React.ComponentPropsWithoutRef<"svg">) {
);
}
const reviews = [
{
name: "Duras",
username: "@duras",
body: "This app convinced me to try something beyond pure Docker Compose. Its a pleasure to contribute to such an awesome project!",
img: "https://avatar.vercel.sh/duras",
},
{
name: "apis",
username: "@apis",
body: "I replaced my previous setup with Dokploy today. Its stable, easy to use, and offers excellent support!",
img: "https://avatar.vercel.sh/apis",
},
{
name: "yayza_",
username: "@yayza_",
body: "Migrated all my services to Dokploy—it worked seamlessly! The level of configuration is perfect for all kinds of projects.",
img: "https://avatar.vercel.sh/yayza",
},
{
name: "Vaurion",
username: "@vaurion",
body: "Dokploy makes my deployments incredibly easy. I just test locally, push a preview to GitHub, and Dokploy takes care of the rest.",
img: "https://avatar.vercel.sh/vaurion",
},
{
name: "vinum?",
username: "@vinum",
body: "Dokploy is everything I wanted in a PaaS. The functionality is impressive, and it's completely free!",
img: "https://avatar.vercel.sh/vinum",
},
{
name: "vadzim",
username: "@vadzim",
body: "Dokploy is fantastic! I rarely encounter any deployment issues, and the community support is top-notch.",
img: "https://avatar.vercel.sh/vadzim",
},
{
name: "Slurpy Beckerman",
username: "@slurpy",
body: "This is exactly what I want in a deployment system. Ive restructured my dev process around Dokploy!",
img: "https://avatar.vercel.sh/slurpy",
},
{
name: "lua",
username: "@lua",
body: "Dokploy is genuinely so nice to use. The hard work behind it really shows.",
img: "https://avatar.vercel.sh/lua",
},
{
name: "johnnygri",
username: "@johnnygri",
body: "Dokploy is a complete joy to use. Im running a mix of critical and low-priority services seamlessly across servers.",
img: "https://avatar.vercel.sh/johnnygri",
},
{
name: "HiJoe",
username: "@hijoe",
body: "Setting up Dokploy was great—simple, intuitive, and reliable. Perfect for small to medium-sized businesses.",
img: "https://avatar.vercel.sh/hijoe",
},
{
name: "johannes0910",
username: "@johannes0910",
body: "Dokploy has been a game-changer for my side projects. Solid UI, straightforward Docker abstraction, and great design.",
img: "https://avatar.vercel.sh/johannes0910",
},
];
const firstRow = reviews.slice(0, reviews.length / 2);
const secondRow = reviews.slice(reviews.length / 2);
const ReviewCard = ({
img,
name,
username,
body,
}: {
img: string;
name: string;
username: string;
body: string;
}) => {
return (
<figure
className={cn(
"relative w-64 cursor-pointer overflow-hidden rounded-xl border p-4",
// light styles
// "border-gray-950/[.1] bg-gray-950/[.01] hover:bg-gray-950/[.05]",
// dark styles
"hover:bg-gray-50/[.15]",
)}
>
<div className="flex flex-row items-center gap-2">
<img className="rounded-full" width="32" height="32" alt="" src={img} />
<div className="flex flex-col">
<figcaption className="text-sm font-medium text-white">
{name}
</figcaption>
<p className="text-xs font-medium text-white/40">{username}</p>
</div>
</div>
<blockquote className="mt-2 text-sm">{body}</blockquote>
</figure>
);
};
export function Testimonials() {
return (
<section
id="testimonials"
aria-label="What our customers are saying"
className="bg-black py-20 sm:py-32"
className=" 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>
<div className="mx-auto max-w-2xl md:text-center">
<h2 className="font-display text-3xl tracking-tight sm:text-4xl">
Why Developers Love Dokploy
</h2>
<p className="mt-4 text-lg tracking-tight text-muted-foreground ">
Think were bragging? Hear from the devs who once doubted toountil
Dokploy made their lives (and deployments) surprisingly easier.
</p>
</div>
<div className="relative flex h-[500px] w-full flex-col items-center justify-center overflow-hidden bg-background md:shadow-xl">
<Marquee pauseOnHover className="[--duration:20s]">
{firstRow.map((review) => (
<ReviewCard key={review.username} {...review} />
))}
</ul>
</Container>
</Marquee>
<Marquee reverse pauseOnHover className="[--duration:20s]">
{secondRow.map((review) => (
<ReviewCard key={review.username} {...review} />
))}
</Marquee>
<div className="pointer-events-none absolute inset-y-0 left-0 w-1/3 bg-gradient-to-r from-background" />
<div className="pointer-events-none absolute inset-y-0 right-0 w-1/3 bg-gradient-to-l from-background" />
</div>
</section>
);
}

View File

@@ -0,0 +1,233 @@
import { cn } from "@/lib/utils";
import { IconBrandYoutubeFilled } from "@tabler/icons-react";
import { motion } from "framer-motion";
import Image from "next/image";
import Link from "next/link";
import type React from "react";
import { useEffect, useRef } from "react";
export function FeaturesSectionDemo() {
const features = [
{
title: "Track issues effectively",
description:
"Track and manage your project issues with ease using our intuitive interface.",
skeleton: <SkeletonOne />,
className:
"col-span-1 lg:col-span-4 border-b lg:border-r dark:border-neutral-800",
},
{
title: "Capture pictures with AI",
description:
"Capture stunning photos effortlessly using our advanced AI technology.",
skeleton: <SkeletonTwo />,
className: "border-b col-span-1 lg:col-span-2 dark:border-neutral-800",
},
{
title: "Watch our AI on YouTube",
description:
"Whether its you or Tyler Durden, you can get to know about our product on YouTube",
skeleton: <SkeletonThree />,
className:
"col-span-1 lg:col-span-3 lg:border-r dark:border-neutral-800",
},
{
title: "Deploy in seconds",
description:
"With our blazing fast, state of the art, cutting edge, we are so back cloud servies (read AWS) - you can deploy your model in seconds.",
skeleton: <SkeletonFour />,
className: "col-span-1 lg:col-span-3 border-b lg:border-none",
},
];
return (
<div className="relative z-20 py-10 lg:py-40 max-w-7xl mx-auto">
<div className="px-8">
<h4 className="text-3xl lg:text-5xl lg:leading-tight max-w-5xl mx-auto text-center tracking-tight font-medium text-black dark:text-white">
Packed with thousands of features
</h4>
<p className="text-sm lg:text-base max-w-2xl my-4 mx-auto text-neutral-500 text-center font-normal dark:text-neutral-300">
From Image generation to video generation, Everything AI has APIs for
literally everything. It can even create this website copy for you.
</p>
</div>
<div className="relative ">
<div className="grid grid-cols-1 lg:grid-cols-6 mt-12 xl:border rounded-md dark:border-neutral-800">
{features.map((feature) => (
<FeatureCard key={feature.title} className={feature.className}>
<FeatureTitle>{feature.title}</FeatureTitle>
<FeatureDescription>{feature.description}</FeatureDescription>
<div className=" h-full w-full">{feature.skeleton}</div>
</FeatureCard>
))}
</div>
</div>
</div>
);
}
const FeatureCard = ({
children,
className,
}: {
children?: React.ReactNode;
className?: string;
}) => {
return (
<div className={cn("p-4 sm:p-8 relative overflow-hidden", className)}>
{children}
</div>
);
};
const FeatureTitle = ({ children }: { children?: React.ReactNode }) => {
return (
<p className=" max-w-5xl mx-auto text-left tracking-tight text-black dark:text-white text-xl md:text-2xl md:leading-snug">
{children}
</p>
);
};
const FeatureDescription = ({ children }: { children?: React.ReactNode }) => {
return (
<p
className={cn(
"text-sm md:text-base max-w-4xl text-left mx-auto",
"text-neutral-500 text-center font-normal dark:text-neutral-300",
"text-left max-w-sm mx-0 md:text-sm my-2",
)}
>
{children}
</p>
);
};
export const SkeletonOne = () => {
return (
<div className="relative flex py-8 px-2 gap-10 h-full">
<div className="w-full p-5 mx-auto bg-white dark:bg-neutral-900 shadow-2xl group h-full">
<div className="flex flex-1 w-full h-full flex-col space-y-2 ">
{/* TODO */}
<Image
src="/linear.webp"
alt="header"
width={800}
height={800}
className="h-full w-full aspect-square object-cover object-left-top rounded-sm"
/>
</div>
</div>
<div className="absolute bottom-0 z-40 inset-x-0 h-60 bg-gradient-to-t from-white dark:from-black via-white dark:via-black to-transparent w-full pointer-events-none" />
<div className="absolute top-0 z-40 inset-x-0 h-60 bg-gradient-to-b from-white dark:from-black via-transparent to-transparent w-full pointer-events-none" />
</div>
);
};
export const SkeletonThree = () => {
return (
<Link
href="https://www.youtube.com/watch?v=RPa3_AD1_Vs"
target="__blank"
className="relative flex gap-10 h-full group/image"
>
<div className="w-full mx-auto bg-transparent dark:bg-transparent group h-full">
<div className="flex flex-1 w-full h-full flex-col space-y-2 relative">
{/* TODO */}
<IconBrandYoutubeFilled className="h-20 w-20 absolute z-10 inset-0 text-red-500 m-auto " />
<Image
src="https://assets.aceternity.com/fireship.jpg"
alt="header"
width={800}
height={800}
className="h-full w-full aspect-square object-cover object-center rounded-sm blur-none group-hover/image:blur-md transition-all duration-200"
/>
</div>
</div>
</Link>
);
};
export const SkeletonTwo = () => {
const images = [
"https://images.unsplash.com/photo-1517322048670-4fba75cbbb62?q=80&w=3000&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
"https://images.unsplash.com/photo-1573790387438-4da905039392?q=80&w=3425&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
"https://images.unsplash.com/photo-1555400038-63f5ba517a47?q=80&w=3540&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
"https://images.unsplash.com/photo-1554931670-4ebfabf6e7a9?q=80&w=3387&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
"https://images.unsplash.com/photo-1546484475-7f7bd55792da?q=80&w=2581&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
];
const imageVariants = {
whileHover: {
scale: 1.1,
rotate: 0,
zIndex: 100,
},
whileTap: {
scale: 1.1,
rotate: 0,
zIndex: 100,
},
};
return (
<div className="relative flex flex-col items-start p-8 gap-10 h-full overflow-hidden">
{/* TODO */}
<div className="flex flex-row -ml-20">
{images.map((image, idx) => (
<motion.div
variants={imageVariants}
key={`images-first${idx}`}
style={{
rotate: Math.random() * 20 - 10,
}}
whileHover="whileHover"
whileTap="whileTap"
className="rounded-xl -mr-4 mt-4 p-1 bg-white dark:bg-neutral-800 dark:border-neutral-700 border border-neutral-100 flex-shrink-0 overflow-hidden"
>
<Image
src={image}
alt="bali images"
width="500"
height="500"
className="rounded-lg h-20 w-20 md:h-40 md:w-40 object-cover flex-shrink-0"
/>
</motion.div>
))}
</div>
<div className="flex flex-row">
{images.map((image, idx) => (
<motion.div
key={`images-second${idx}`}
style={{
rotate: Math.random() * 20 - 10,
}}
variants={imageVariants}
whileHover="whileHover"
whileTap="whileTap"
className="rounded-xl -mr-4 mt-4 p-1 bg-white dark:bg-neutral-800 dark:border-neutral-700 border border-neutral-100 flex-shrink-0 overflow-hidden"
>
<Image
src={image}
alt="bali images"
width="500"
height="500"
className="rounded-lg h-20 w-20 md:h-40 md:w-40 object-cover flex-shrink-0"
/>
</motion.div>
))}
</div>
<div className="absolute left-0 z-[100] inset-y-0 w-20 bg-gradient-to-r from-white dark:from-black to-transparent h-full pointer-events-none" />
<div className="absolute right-0 z-[100] inset-y-0 w-20 bg-gradient-to-l from-white dark:from-black to-transparent h-full pointer-events-none" />
</div>
);
};
export const SkeletonFour = () => {
return (
<div className="h-60 md:h-60 flex flex-col items-center relative bg-transparent dark:bg-transparent mt-10">
{/* <Globe className="absolute -right-10 md:-right-10 -bottom-80 md:-bottom-72" /> */}
</div>
);
};

View File

@@ -0,0 +1,148 @@
"use client";
import { cn } from "@/lib/utils";
import {
IconActivity,
IconCloud,
IconDatabase,
IconEaseInOut,
IconRocket,
IconTemplate,
IconTerminal,
IconTerminal2,
IconUsers,
} from "@tabler/icons-react";
import { Layers, Lock, UnlockIcon } from "lucide-react";
export function FeaturesSectionDemo() {
const features = [
{
title: "Flexible Application Deployment",
description:
"Deploy any application using Nixpacks, Heroku Buildpacks, or your custom Dockerfile, tailored to your stack.",
icon: <IconRocket />,
},
{
title: "Native Docker Compose Support",
description:
"Deploy complex applications natively with full Docker Compose integration for seamless orchestration.",
icon: <Layers />,
},
{
title: "Multi-server Support",
description:
"Effortlessly deploy your applications on remote servers, with zero configuration hassle.",
icon: <IconCloud />,
},
{
title: "Advanced User Management",
description:
"Control user access with detailed roles and permissions, keeping your deployments secure and organized.",
icon: <IconUsers />,
},
{
title: "Database Management with Backups",
description:
"Manage and back up MySQL, PostgreSQL, MongoDB, MariaDB, Redis directly from Dokploy.",
icon: <IconDatabase />,
},
{
title: "API & CLI Access",
description:
"Need custom functionality? Dokploy offers complete API and CLI access to fit your needs.",
icon: <IconTerminal />,
},
{
title: "Docker Swarm Clusters",
description:
"Scale your deployments seamlessly with built-in Docker Swarm support for robust, multi-node applications.",
icon: <IconUsers />,
},
{
title: "Open Source Templates",
description:
"Get started quickly with pre-configured templates for popular tools like Supabase, Cal.com, and Pocketbase.",
icon: <IconTemplate />,
},
{
title: "No Vendor Lock-In",
description:
"Experience complete freedom to modify, scale, and customize Dokploy to suit your specific needs.",
icon: <UnlockIcon />,
},
{
title: "Real-time Monitoring & Alerts",
description:
"Monitor CPU, memory, and network usage in real-time across your deployments for full visibility.",
icon: <IconActivity />,
},
{
title: "Built for developers",
description:
"Designed specifically for engineers and developers seeking control and flexibility.",
icon: <IconTerminal2 />,
},
{
title: "Self-hosted & Open Source",
description:
"Dokploy provides complete control with self-hosting capabilities and open-source transparency.",
icon: <IconEaseInOut />,
},
];
return (
<div className="flex flex-col justify-center items-center mt-20 px-4">
<h2 className="font-display text-3xl tracking-tight text-primary sm:text-4xl text-center">
Powerful Deployment, Tailored for You
</h2>
<p className="mt-4 text-lg tracking-tight text-muted-foreground text-center">
Unlock seamless multi-server deployments, advanced user control, and
flexible database managementall with Dokploys developer-focused
features.
</p>
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 relative z-10 py-10 max-w-7xl mx-auto mt-10">
{features.map((feature, index) => (
<Feature key={feature.title} {...feature} index={index} />
))}
</div>
</div>
);
}
const Feature = ({
title,
description,
icon,
index,
}: {
title: string;
description: string;
icon: React.ReactNode;
index: number;
}) => {
return (
<div
className={cn(
"flex flex-col lg:border-r py-10 relative group/feature border-neutral-800",
(index === 0 || index === 4 || index === 8) &&
"lg:border-l dark:border-neutral-800",
(index < 4 || index < 8) && "lg:border-b dark:border-neutral-800",
)}
>
{index < 4 && (
<div className="opacity-0 group-hover/feature:opacity-100 transition duration-200 absolute inset-0 h-full w-full bg-gradient-to-t from-neutral-800 to-transparent pointer-events-none" />
)}
{index >= 4 && (
<div className="opacity-0 group-hover/feature:opacity-100 transition duration-200 absolute inset-0 h-full w-full bg-gradient-to-b from-neutral-800 to-transparent pointer-events-none" />
)}
<div className="mb-4 relative z-10 px-10 text-neutral-400">{icon}</div>
<div className="text-lg font-bold mb-2 relative z-10 px-10">
<div className="absolute left-0 inset-y-0 h-6 group-hover/feature:h-8 w-1 rounded-tr-full rounded-br-full bg-neutral-700 group-hover/feature:bg-white transition-all duration-200 origin-center" />
<span className="group-hover/feature:translate-x-2 transition duration-200 inline-block text-neutral-100">
{title}
</span>
</div>
<p className="text-sm text-neutral-300 max-w-xs relative z-10 px-10">
{description}
</p>
</div>
);
};

View File

@@ -0,0 +1,197 @@
import { ExternalLink } from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { cn } from "@/lib/utils";
import { Button, buttonVariants } from "../ui/button";
const Hero12 = () => {
return (
<section className="relative overflow-hidden py-32 bg-black">
<div className="absolute inset-0 overflow-hidden">
<div className="flex h-full flex-col items-center justify-end">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 1400 900"
className="-mx-[theme(container.padding)] h-full w-[calc(100%+2*theme(container.padding))]"
>
<defs>
<filter id="blur1" x="-20%" y="-20%" width="140%" height="140%">
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feBlend
mode="normal"
in="SourceGraphic"
in2="BackgroundImageFix"
result="shape"
/>
<feGaussianBlur
stdDeviation="200"
result="effect1_foregroundBlur"
/>
</filter>
<pattern
id="innerGrid"
width="40"
height="40"
patternUnits="userSpaceOnUse"
>
<path
d="M 40 0 L 0 0 0 40"
fill="none"
stroke="hsl(var(--background))"
stroke-width="0.5"
stroke-opacity="0.6"
/>
</pattern>
<pattern
id="grid"
width="160"
height="160"
patternUnits="userSpaceOnUse"
>
<rect width="160" height="160" fill="url(#innerGrid)" />
<path
d="M 70 80 H 90 M 80 70 V 90"
fill="none"
stroke="hsl(var(--background))"
stroke-width="1"
stroke-opacity="0.3"
/>
</pattern>
</defs>
<g filter="url(#blur1)">
<rect width="1400" height="900" fill="hsl(var(--background))" />
<circle
cx="400"
cy="740"
fill="hsl(var(--primary)/0.2)"
r="300"
/>
<circle
cx="1100"
cy="600"
fill="hsl(var(--primary)/0.3)"
r="240"
/>
</g>
<rect width="1400" height="900" fill="url(#grid)" />
</svg>
</div>
</div>
<div className="container relative flex flex-col items-center text-center">
<h1 className="my-3 text-pretty text-2xl font-bold sm:text-4xl md:my-6 lg:text-5xl">
Welcome to Our Website
</h1>
<p className="text-muted-foreground mb-6 max-w-xl md:mb-12 lg:text-xl">
Elig doloremque mollitia fugiat omnis! Porro facilis quo animi
consequatur.
</p>
<div className="mb-16 space-y-8">
<div className="flex items-start gap-4">
{/* <div className="flex flex-col items-center gap-2">
<button
type
className="ring-offset-background focus-visible:ring-ring border-input bg-background hover:bg-accent hover:text-accent-foreground inline-flex h-10 items-center justify-center whitespace-nowrap rounded-md border px-4 py-2 text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50">
Try our product for free
</button>
<p className="text-muted-foreground text-xs">
No credit card required
</p>
</div>
<button className="ring-offset-background focus-visible:ring-ring bg-primary text-primary-foreground hover:bg-primary/90 inline-flex h-10 items-center justify-center whitespace-nowrap rounded-md px-4 py-2 text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50">
Book a demo
</button> */}
</div>
<div className="bg-background flex w-full items-center justify-between rounded-full px-4 py-2 shadow">
<div className="flex items-center">
<span className="flex items-center gap-x-0.5">
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
className="lucide lucide-star fill-accent-foreground stroke-accent-foreground size-3"
>
<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" />
</svg>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
className="lucide lucide-star fill-accent-foreground stroke-accent-foreground size-3"
>
<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" />
</svg>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
className="lucide lucide-star fill-accent-foreground stroke-accent-foreground size-3"
>
<polygon points="12 2 15.09 8.`26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" />
</svg>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
className="lucide lucide-star fill-accent-foreground stroke-accent-foreground size-3"
>
<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" />
</svg>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
className="lucide lucide-star fill-accent-foreground stroke-accent-foreground size-3"
>
<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" />
</svg>
</span>
<span className="ml-2 text-xs">4.8 starts</span>
</div>
<div className="text-xs">207 Reviews</div>
</div>
</div>
</div>
<div className="container relative -mb-48 overflow-hidden">
<div className="border-background/40 bg-background/20 mx-auto aspect-[4/3] max-w-3xl rounded-xl border p-4">
<img
src="https://www.shadcnblocks.com/images/block/placeholder-1.svg"
alt="placeholder hero"
className="size-full rounded-md object-cover"
/>
</div>
</div>
</section>
);
};
export default Hero12;

View File

@@ -0,0 +1,61 @@
"use client";
import * as AccordionPrimitive from "@radix-ui/react-accordion";
import { ChevronDown, Minus, PlusIcon } from "lucide-react";
import * as React from "react";
import { cn } from "@/lib/utils";
const Accordion = AccordionPrimitive.Root;
const AccordionItem = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
>(({ className, ...props }, ref) => (
<AccordionPrimitive.Item
ref={ref}
className={cn("border-b", className)}
{...props}
/>
));
AccordionItem.displayName = "AccordionItem";
const AccordionTrigger = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
>(({ className, children, ...props }, ref) => (
<AccordionPrimitive.Header className="flex">
<AccordionPrimitive.Trigger
ref={ref}
className={cn(
"flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180 group",
className,
)}
{...props}
>
{children}
<PlusIcon className="h-4 w-4 shrink-0 transition-transform duration-200 group-data-[state=open]:hidden" />
<Minus className="h-4 w-4 shrink-0 transition-transform duration-200 group-data-[state=closed]:hidden" />
</AccordionPrimitive.Trigger>
</AccordionPrimitive.Header>
));
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;
const AccordionContent = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
>(({ className, children, ...props }, ref) => (
<AccordionPrimitive.Content
ref={ref}
className="overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
{...props}
>
<div className={cn("pb-4 pt-0 text-muted-foreground", className)}>
{children}
</div>
</AccordionPrimitive.Content>
));
AccordionContent.displayName = AccordionPrimitive.Content.displayName;
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };

View File

@@ -0,0 +1,28 @@
import type { ReactNode } from "react";
import { cn } from "@/lib/utils";
export default function AnimatedGradientText({
children,
className,
}: {
children: ReactNode;
className?: string;
}) {
return (
<div
className={cn(
"group relative mx-auto flex max-w-fit flex-row items-center justify-center rounded-2xl px-4 py-1.5 text-sm font-medium shadow-[inset_0_-8px_10px_#8fdfff1f] backdrop-blur-sm transition-shadow duration-500 ease-out [--bg-size:300%] hover:shadow-[inset_0_-5px_10px_#8fdfff3f] bg-black/40",
className,
)}
>
<div
className={
"absolute inset-0 block h-full w-full animate-gradient bg-gradient-to-r from-[#ffaa40]/50 via-[#9c40ff]/50 to-[#ffaa40]/50 bg-[length:var(--bg-size)_100%] p-[1px] ![mask-composite:subtract] [border-radius:inherit] [mask:linear-gradient(#fff_0_0)_content-box,linear-gradient(#fff_0_0)]"
}
/>
{children}
</div>
);
}

View File

@@ -0,0 +1,143 @@
"use client";
import { AnimatePresence, motion } from "framer-motion";
import { Play, XIcon } from "lucide-react";
import { useState } from "react";
import { cn } from "@/lib/utils";
type AnimationStyle =
| "from-bottom"
| "from-center"
| "from-top"
| "from-left"
| "from-right"
| "fade"
| "top-in-bottom-out"
| "left-in-right-out";
interface HeroVideoProps {
animationStyle?: AnimationStyle;
videoSrc: string;
thumbnailSrc: string;
thumbnailAlt?: string;
className?: string;
}
const animationVariants = {
"from-bottom": {
initial: { y: "100%", opacity: 0 },
animate: { y: 0, opacity: 1 },
exit: { y: "100%", opacity: 0 },
},
"from-center": {
initial: { scale: 0.5, opacity: 0 },
animate: { scale: 1, opacity: 1 },
exit: { scale: 0.5, opacity: 0 },
},
"from-top": {
initial: { y: "-100%", opacity: 0 },
animate: { y: 0, opacity: 1 },
exit: { y: "-100%", opacity: 0 },
},
"from-left": {
initial: { x: "-100%", opacity: 0 },
animate: { x: 0, opacity: 1 },
exit: { x: "-100%", opacity: 0 },
},
"from-right": {
initial: { x: "100%", opacity: 0 },
animate: { x: 0, opacity: 1 },
exit: { x: "100%", opacity: 0 },
},
fade: {
initial: { opacity: 0 },
animate: { opacity: 1 },
exit: { opacity: 0 },
},
"top-in-bottom-out": {
initial: { y: "-100%", opacity: 0 },
animate: { y: 0, opacity: 1 },
exit: { y: "100%", opacity: 0 },
},
"left-in-right-out": {
initial: { x: "-100%", opacity: 0 },
animate: { x: 0, opacity: 1 },
exit: { x: "100%", opacity: 0 },
},
};
export default function HeroVideoDialog({
animationStyle = "from-center",
videoSrc,
thumbnailSrc,
thumbnailAlt = "Video thumbnail",
className,
}: HeroVideoProps) {
const [isVideoOpen, setIsVideoOpen] = useState(false);
const selectedAnimation = animationVariants[animationStyle];
return (
<div className={cn("relative", className)}>
<div
className="relative cursor-pointer group"
onClick={() => setIsVideoOpen(true)}
>
<img
src={thumbnailSrc}
alt={thumbnailAlt}
width={1920}
height={1080}
className="w-full transition-all duration-200 group-hover:brightness-[0.8] ease-out rounded-md shadow-lg border"
/>
<div className="absolute inset-0 flex items-center justify-center group-hover:scale-100 scale-[0.9] transition-all duration-200 ease-out rounded-2xl">
<div className="bg-primary/10 flex items-center justify-center rounded-full backdrop-blur-md size-28">
<div
className={
"flex items-center justify-center bg-gradient-to-b from-primary/30 to-primary shadow-md rounded-full size-20 transition-all ease-out duration-200 relative group-hover:scale-[1.2] scale-100"
}
>
<Play
className="size-8 text-white fill-white group-hover:scale-105 scale-100 transition-transform duration-200 ease-out"
style={{
filter:
"drop-shadow(0 4px 3px rgb(0 0 0 / 0.07)) drop-shadow(0 2px 2px rgb(0 0 0 / 0.06))",
}}
/>
</div>
</div>
</div>
</div>
<AnimatePresence>
{isVideoOpen && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
onClick={() => setIsVideoOpen(false)}
exit={{ opacity: 0 }}
className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-md"
>
<motion.div
{...selectedAnimation}
transition={{ type: "spring", damping: 30, stiffness: 300 }}
className="relative w-full max-w-4xl aspect-video mx-4 md:mx-0"
>
<motion.button className="absolute -top-16 right-0 text-white text-xl bg-neutral-900/50 ring-1 backdrop-blur-md rounded-full p-2 dark:bg-neutral-100/50 dark:text-black">
<XIcon className="size-5" />
</motion.button>
<div className="size-full border-2 border-white rounded-2xl overflow-hidden isolate z-[1] relative">
{/* biome-ignore lint/a11y/useIframeTitle: <explanation> */}
<iframe
src={videoSrc}
className="size-full rounded-2xl"
allowFullScreen
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
/>
</div>
</motion.div>
</motion.div>
)}
</AnimatePresence>
</div>
);
}

View File

@@ -0,0 +1,51 @@
import { cn } from "@/lib/utils";
interface MarqueeProps {
className?: string;
reverse?: boolean;
pauseOnHover?: boolean;
children?: React.ReactNode;
vertical?: boolean;
repeat?: number;
[key: string]: any;
}
export function Marquee({
className,
reverse,
pauseOnHover = false,
children,
vertical = false,
repeat = 4,
...props
}: MarqueeProps) {
return (
<div
{...props}
className={cn(
"group flex overflow-hidden p-2 [--duration:40s] [--gap:1rem] [gap:var(--gap)]",
{
"flex-row": !vertical,
"flex-col": vertical,
},
className,
)}
>
{Array(repeat)
.fill(0)
.map((_, i) => (
<div
key={i}
className={cn("flex shrink-0 justify-around [gap:var(--gap)]", {
"animate-marquee flex-row": !vertical,
"animate-marquee-vertical flex-col": vertical,
"group-hover:[animation-play-state:paused]": pauseOnHover,
"[animation-direction:reverse]": reverse,
})}
>
{children}
</div>
))}
</div>
);
}

View File

@@ -0,0 +1,138 @@
import type { SVGProps } from "react";
export interface SafariProps extends SVGProps<SVGSVGElement> {
url?: string;
src?: string;
width?: number;
height?: number;
}
export default function Safari({
src,
url,
width = 1203,
height = 753,
...props
}: SafariProps) {
return (
<svg
width={width}
height={height}
viewBox={`0 0 ${width} ${height}`}
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<g clipPath="url(#path0)">
<path
d="M0 52H1202V741C1202 747.627 1196.63 753 1190 753H12C5.37258 753 0 747.627 0 741V52Z"
className="fill-[#404040]"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M0 12C0 5.37258 5.37258 0 12 0H1190C1196.63 0 1202 5.37258 1202 12V52H0L0 12Z"
className="fill-[#404040]"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M1.06738 12C1.06738 5.92487 5.99225 1 12.0674 1H1189.93C1196.01 1 1200.93 5.92487 1200.93 12V51H1.06738V12Z"
className="fill-[#262626]"
/>
<circle cx="27" cy="25" r="6" className="fill-[#404040]" />
<circle cx="47" cy="25" r="6" className="fill-[#404040]" />
<circle cx="67" cy="25" r="6" className="fill-[#404040]" />
<path
d="M286 17C286 13.6863 288.686 11 292 11H946C949.314 11 952 13.6863 952 17V35C952 38.3137 949.314 41 946 41H292C288.686 41 286 38.3137 286 35V17Z"
className="fill-[#404040]"
/>
<g className="mix-blend-luminosity">
<path
d="M566.269 32.0852H572.426C573.277 32.0852 573.696 31.6663 573.696 30.7395V25.9851C573.696 25.1472 573.353 24.7219 572.642 24.6521V23.0842C572.642 20.6721 571.036 19.5105 569.348 19.5105C567.659 19.5105 566.053 20.6721 566.053 23.0842V24.6711C565.393 24.7727 565 25.1917 565 25.9851V30.7395C565 31.6663 565.418 32.0852 566.269 32.0852ZM567.272 22.97C567.272 21.491 568.211 20.6785 569.348 20.6785C570.478 20.6785 571.423 21.491 571.423 22.97V24.6394L567.272 24.6458V22.97Z"
fill="#A3A3A3"
/>
</g>
<g className="mix-blend-luminosity">
<text
x="580"
y="30"
fill="#A3A3A3"
fontSize="12"
fontFamily="Arial, sans-serif"
>
{url}
</text>
</g>
<g className="mix-blend-luminosity">
<path
d="M265.5 33.8984C265.641 33.8984 265.852 33.8516 266.047 33.7422C270.547 31.2969 272.109 30.1641 272.109 27.3203V21.4219C272.109 20.4844 271.742 20.1484 270.961 19.8125C270.094 19.4453 267.18 18.4297 266.328 18.1406C266.07 18.0547 265.766 18 265.5 18C265.234 18 264.93 18.0703 264.672 18.1406C263.82 18.3828 260.906 19.4531 260.039 19.8125C259.258 20.1406 258.891 20.4844 258.891 21.4219V27.3203C258.891 30.1641 260.461 31.2812 264.945 33.7422C265.148 33.8516 265.359 33.8984 265.5 33.8984ZM265.922 19.5781C266.945 19.9766 269.172 20.7656 270.344 21.1875C270.562 21.2656 270.617 21.3828 270.617 21.6641V27.0234C270.617 29.3125 269.469 29.9375 265.945 32.0625C265.727 32.1875 265.617 32.2344 265.508 32.2344V19.4844C265.617 19.4844 265.734 19.5156 265.922 19.5781Z"
fill="#A3A3A3"
/>
</g>
<g className="mix-blend-luminosity">
<path
d="M936.273 24.9766C936.5 24.9766 936.68 24.9062 936.82 24.7578L940.023 21.5312C940.195 21.3594 940.273 21.1719 940.273 20.9531C940.273 20.7422 940.188 20.5391 940.023 20.3828L936.82 17.125C936.68 16.9688 936.5 16.8906 936.273 16.8906C935.852 16.8906 935.516 17.2422 935.516 17.6719C935.516 17.8828 935.594 18.0547 935.727 18.2031L937.594 20.0312C937.227 19.9766 936.852 19.9453 936.477 19.9453C932.609 19.9453 929.516 23.0391 929.516 26.9141C929.516 30.7891 932.633 33.9062 936.5 33.9062C940.375 33.9062 943.484 30.7891 943.484 26.9141C943.484 26.4453 943.156 26.1094 942.688 26.1094C942.234 26.1094 941.93 26.4453 941.93 26.9141C941.93 29.9297 939.516 32.3516 936.5 32.3516C933.492 32.3516 931.07 29.9297 931.07 26.9141C931.07 23.875 933.469 21.4688 936.477 21.4688C936.984 21.4688 937.453 21.5078 937.867 21.5781L935.734 23.6875C935.594 23.8281 935.516 24 935.516 24.2109C935.516 24.6406 935.852 24.9766 936.273 24.9766Z"
fill="#A3A3A3"
/>
</g>
<g className="mix-blend-luminosity">
<path
d="M1134 33.0156C1134.49 33.0156 1134.89 32.6094 1134.89 32.1484V27.2578H1139.66C1140.13 27.2578 1140.54 26.8594 1140.54 26.3672C1140.54 25.8828 1140.13 25.4766 1139.66 25.4766H1134.89V20.5859C1134.89 20.1172 1134.49 19.7188 1134 19.7188C1133.52 19.7188 1133.11 20.1172 1133.11 20.5859V25.4766H1128.34C1127.88 25.4766 1127.46 25.8828 1127.46 26.3672C1127.46 26.8594 1127.88 27.2578 1128.34 27.2578H1133.11V32.1484C1133.11 32.6094 1133.52 33.0156 1134 33.0156Z"
fill="#A3A3A3"
/>
</g>
<g className="mix-blend-luminosity">
<path
d="M1161.8 31.0703H1163.23V32.375C1163.23 34.0547 1164.12 34.9219 1165.81 34.9219H1174.2C1175.89 34.9219 1176.77 34.0547 1176.77 32.3828V24.0469C1176.77 22.375 1175.89 21.5 1174.2 21.5H1172.77V20.2578C1172.77 18.5859 1171.88 17.7109 1170.19 17.7109H1161.8C1160.1 17.7109 1159.23 18.5781 1159.23 20.2578V28.5234C1159.23 30.1953 1160.1 31.0703 1161.8 31.0703ZM1161.9 29.5078C1161.18 29.5078 1160.78 29.1328 1160.78 28.3828V20.3984C1160.78 19.6406 1161.18 19.2656 1161.9 19.2656H1170.09C1170.8 19.2656 1171.2 19.6406 1171.2 20.3984V21.5H1165.81C1164.12 21.5 1163.23 22.375 1163.23 24.0469V29.5078H1161.9ZM1165.91 33.3672C1165.19 33.3672 1164.8 32.9922 1164.8 32.2422V24.1875C1164.8 23.4297 1165.19 23.0625 1165.91 23.0625H1174.1C1174.81 23.0625 1175.21 23.4297 1175.21 24.1875V32.2422C1175.21 32.9922 1174.81 33.3672 1174.1 33.3672H1165.91Z"
fill="#A3A3A3"
/>
</g>
<g className="mix-blend-luminosity">
<path
d="M1099.51 28.4141C1099.91 28.4141 1100.24 28.0859 1100.24 27.6953V19.8359L1100.18 18.6797L1100.66 19.25L1101.75 20.4141C1101.88 20.5547 1102.06 20.625 1102.24 20.625C1102.6 20.625 1102.9 20.3672 1102.9 20C1102.9 19.8047 1102.82 19.6641 1102.69 19.5312L1100.06 17.0078C1099.88 16.8203 1099.7 16.7578 1099.51 16.7578C1099.32 16.7578 1099.14 16.8203 1098.95 17.0078L1096.33 19.5312C1096.2 19.6641 1096.12 19.8047 1096.12 20C1096.12 20.3672 1096.41 20.625 1096.77 20.625C1096.95 20.625 1097.14 20.5547 1097.27 20.4141L1098.35 19.25L1098.84 18.6719L1098.78 19.8359V27.6953C1098.78 28.0859 1099.11 28.4141 1099.51 28.4141ZM1095 34.6562H1104C1105.7 34.6562 1106.57 33.7812 1106.57 32.1094V24.4297C1106.57 22.7578 1105.7 21.8828 1104 21.8828H1101.89V23.4375H1103.9C1104.61 23.4375 1105.02 23.8125 1105.02 24.5625V31.9688C1105.02 32.7188 1104.61 33.0938 1103.9 33.0938H1095.1C1094.38 33.0938 1093.98 32.7188 1093.98 31.9688V24.5625C1093.98 23.8125 1094.38 23.4375 1095.1 23.4375H1097.13V21.8828H1095C1093.31 21.8828 1092.43 22.75 1092.43 24.4297V32.1094C1092.43 33.7812 1093.31 34.6562 1095 34.6562Z"
fill="#A3A3A3"
/>
</g>
<g className="mix-blend-luminosity">
<path
d="M99.5703 33.6016H112.938C114.633 33.6016 115.516 32.7266 115.516 31.0547V21.5469C115.516 19.875 114.633 19 112.938 19H99.5703C97.8828 19 97 19.8672 97 21.5469V31.0547C97 32.7266 97.8828 33.6016 99.5703 33.6016ZM99.6719 32.0469C98.9531 32.0469 98.5547 31.6719 98.5547 30.9141V21.6875C98.5547 20.9297 98.9531 20.5547 99.6719 20.5547H103.234V32.0469H99.6719ZM112.836 20.5547C113.555 20.5547 113.953 20.9297 113.953 21.6875V30.9141C113.953 31.6719 113.555 32.0469 112.836 32.0469H104.711V20.5547H112.836ZM101.703 23.4141C101.984 23.4141 102.219 23.1719 102.219 22.9062C102.219 22.6406 101.984 22.4062 101.703 22.4062H100.102C99.8203 22.4062 99.5859 22.6406 99.5859 22.9062C99.5859 23.1719 99.8203 23.4141 100.102 23.4141H101.703ZM101.703 25.5156C101.984 25.5156 102.219 25.2812 102.219 25.0078C102.219 24.7422 101.984 24.5078 101.703 24.5078H100.102C99.8203 24.5078 99.5859 24.7422 99.5859 25.0078C99.5859 25.2812 99.8203 25.5156 100.102 25.5156H101.703ZM101.703 27.6094C101.984 27.6094 102.219 27.3828 102.219 27.1094C102.219 26.8438 101.984 26.6172 101.703 26.6172H100.102C99.8203 26.6172 99.5859 26.8438 99.5859 27.1094C99.5859 27.3828 99.8203 27.6094 100.102 27.6094H101.703Z"
fill="#A3A3A3"
/>
</g>
<g className="mix-blend-luminosity">
<path
d="M143.914 32.5938C144.094 32.7656 144.312 32.8594 144.562 32.8594C145.086 32.8594 145.492 32.4531 145.492 31.9375C145.492 31.6797 145.391 31.4453 145.211 31.2656L139.742 25.9219L145.211 20.5938C145.391 20.4141 145.492 20.1719 145.492 19.9219C145.492 19.4062 145.086 19 144.562 19C144.312 19 144.094 19.0938 143.922 19.2656L137.844 25.2031C137.625 25.4062 137.516 25.6562 137.516 25.9297C137.516 26.2031 137.625 26.4375 137.836 26.6484L143.914 32.5938Z"
fill="#A3A3A3"
/>
</g>
<g className="mix-blend-luminosity">
<path
d="M168.422 32.8594C168.68 32.8594 168.891 32.7656 169.07 32.5938L175.148 26.6562C175.359 26.4375 175.469 26.2109 175.469 25.9297C175.469 25.6562 175.367 25.4141 175.148 25.2109L169.07 19.2656C168.891 19.0938 168.68 19 168.422 19C167.898 19 167.492 19.4062 167.492 19.9219C167.492 20.1719 167.602 20.4141 167.773 20.5938L173.25 25.9375L167.773 31.2656C167.594 31.4531 167.492 31.6797 167.492 31.9375C167.492 32.4531 167.898 32.8594 168.422 32.8594Z"
fill="#A3A3A3"
/>
</g>
<image
href={src}
width="1200"
height="700"
x="1"
y="52"
preserveAspectRatio="xMidYMid slice"
clipPath="url(#roundedBottom)"
/>
</g>
<defs>
<clipPath id="path0">
<rect width={width} height={height} fill="white" />
</clipPath>
<clipPath id="roundedBottom">
<path
d="M1 52H1201V741C1201 747.075 1196.08 752 1190 752H12C5.92486 752 1 747.075 1 741V52Z"
fill="white"
/>
</clipPath>
</defs>
</svg>
);
}

View File

@@ -39,14 +39,16 @@
"projectsDes": "Manage and organize all your projects in one place, keeping detailed track of progress and resource allocation.",
"applications": "Applications & Databases",
"applicationsDes": "Centralize control over your applications and databases for enhanced security and efficiency, simplifying access and management across your infrastructure.",
"compose": "Compose",
"compose": "Docker Compose",
"composeDes": "Native Docker Compose support for manage complex applications and services with ease.",
"multinode": "Multinode",
"multinodeDes": "Scale applications to multiples nodes using docker swarm to manage the cluster.",
"multiserver": "Multiserver",
"multiserverDes": "Deploy applications to multiple servers without effort.",
"monitoring": "Monitoring",
"monitoringDes": "Monitor your systems' performance and health in real time, ensuring continuous and uninterrupted operation.",
"backups": "Backups",
"backupsDes": "Implement automatic and secure backup solutions to protect your critical data and restore it quickly when necessary."
"backupsDes": "Implement automatic and secure backup solutions to protect your critical data and restore it quickly when necessary.",
"traefik": "Traefik",
"traefikDes": "Manage traefik via File Editor to configure your own domain names, certificates, and more."
},
"secondaryFeatures": {
"title": "Advanced Management Tools",
@@ -65,8 +67,8 @@
"terminalDes": "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"
},
"callToAction": {
"title": "Unlock Your Deployment Potential",
"des": "Streamline your deployments with our PaaS. Effortlessly manage Docker containers and traffic with Traefik. Boost your infrastructure's efficiency and security today",
"title": "Unlock Your Deployment Potential with Dokploy Cloud",
"des": "Say goodbye to infrastructure hassles—Dokploy Cloud handles it all. Effortlessly deploy, manage Docker containers, and secure your traffic with Traefik. Focus on building, well handle the rest.",
"button": "Get Started Now"
},
"faq": {
@@ -76,22 +78,20 @@
"a1": "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.",
"q2": "Why Choose Dokploy?",
"a2": "Dokploy offers simplicity, flexibility, and speed in application deployment and management.",
"q3": "Is Dokploy free?",
"a3": "Yes, Dokploy is totally free. You can use it for personal projects, small teams, or even for large-scale applications.",
"q4": "Is it open source?",
"a4": "Yes, Dokploy is open source and free to use.",
"q5": "What types of languages can I deploy with Dokploy?",
"a5": "Dokploy does not restrict programming languages. You are free to choose your preferred language and framework.",
"q6": "How do I request a feature or report a bug?",
"a6": "To request a feature or report a bug, please create an issue on our GitHub repository or ask in our Discord channel. We are currently focused on addressing existing bugs and plan to release new features soon.",
"a6": "To request a feature or report a bug, please create an issue on our GitHub repository or ask in our Discord channel.",
"q7": "Do you track the usage of Dokploy?",
"a7": "No, we don't track any usage data.",
"q8": "Are there any user forums or communities where I can interact with other users?",
"a8": "Yes, we have active GitHub discussions where you can share ideas, ask for help, and connect with other users.",
"a8": "Yes, we have active GitHub discussions and Discord where you can share ideas, ask for help, and connect with other users.",
"q9": "What types of applications can I deploy with Dokploy?",
"a9": "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.",
"a9": "You can deploy any application that can be Dockerized, with no limits. Dokploy supports builds from Git repositories, Dockerfiles, Nixpacks, and Buildpacks like Heroku and Paketo.",
"q10": "How does Dokploy handle database management?",
"a10": "Dokploy supports multiple database systems including Postgres, MySQL, MariaDB, MongoDB, and Redis, providing tools for easy deployment and management directly from the dashboard."
"a10": "Dokploy supports multiple database systems including Postgres, MySQL, MariaDB, MongoDB, and Redis, providing tools for easy deployment and management and backups directly from the dashboard."
},
"footer": {
"copyright": "Copyright © {year} Dokploy. All rights reserved."

View File

@@ -14,14 +14,17 @@
"dependencies": {
"@headlessui/react": "^1.7.17",
"@headlessui/tailwindcss": "^0.2.0",
"@radix-ui/react-accordion": "^1.2.1",
"@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-switch": "^1.0.3",
"@radix-ui/react-tabs": "1.1.1",
"@tabler/icons-react": "3.21.0",
"@types/node": "20.4.6",
"autoprefixer": "^10.4.12",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.0",
"framer-motion": "^11.0.24",
"framer-motion": "^11.3.19",
"lucide-react": "0.364.0",
"next": "14.2.2",
"next-intl": "^3.19.0",
@@ -31,8 +34,7 @@
"tailwind-merge": "^2.2.2",
"tailwindcss": "^3.4.1",
"tailwindcss-animate": "^1.0.7",
"typescript": "5.1.6",
"@radix-ui/react-tabs": "1.1.1"
"typescript": "5.1.6"
},
"devDependencies": {
"@biomejs/biome": "1.7.0",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 149 KiB

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 KiB

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

View File

@@ -26,7 +26,7 @@ const config = {
"9xl": ["8rem", { lineHeight: "1" }],
},
container: {
center: true,
center: "true",
padding: "2rem",
screens: {
"2xl": "1400px",
@@ -79,6 +79,22 @@ const config = {
display: "var(--font-lexend)",
},
keyframes: {
marquee: {
from: {
transform: "translateX(0)",
},
to: {
transform: "translateX(calc(-100% - var(--gap)))",
},
},
"marquee-vertical": {
from: {
transform: "translateY(0)",
},
to: {
transform: "translateY(calc(-100% - var(--gap)))",
},
},
"accordion-down": {
from: {
height: "0",
@@ -103,11 +119,19 @@ const config = {
"background-position": "calc(100% + var(--shiny-width)) 0",
},
},
gradient: {
to: {
backgroundPosition: "var(--bg-size) 0",
},
},
},
animation: {
"accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out",
"shiny-text": "shiny-text 8s infinite",
marquee: "marquee var(--duration) linear infinite",
"marquee-vertical": "marquee-vertical var(--duration) linear infinite",
gradient: "gradient 8s linear infinite",
},
},
},

82
pnpm-lock.yaml generated
View File

@@ -88,6 +88,9 @@ importers:
'@headlessui/tailwindcss':
specifier: ^0.2.0
version: 0.2.1(tailwindcss@3.4.7)
'@radix-ui/react-accordion':
specifier: ^1.2.1
version: 1.2.1(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@radix-ui/react-select':
specifier: ^2.0.0
version: 2.1.1(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
@@ -100,6 +103,9 @@ importers:
'@radix-ui/react-tabs':
specifier: 1.1.1
version: 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@tabler/icons-react':
specifier: 3.21.0
version: 3.21.0(react@18.2.0)
'@types/node':
specifier: 20.4.6
version: 20.4.6
@@ -113,7 +119,7 @@ importers:
specifier: ^2.1.0
version: 2.1.1
framer-motion:
specifier: ^11.0.24
specifier: ^11.3.19
version: 11.3.19(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
lucide-react:
specifier: 0.364.0
@@ -586,6 +592,19 @@ packages:
'@types/react-dom':
optional: true
'@radix-ui/react-accordion@1.2.1':
resolution: {integrity: sha512-bg/l7l5QzUjgsh8kjwDFommzAshnUsuVMV5NM56QVCm+7ZckYdd9P/ExR8xG/Oup0OajVxNLaHJ1tb8mXk+nzQ==}
peerDependencies:
'@types/react': 18.3.5
'@types/react-dom': 18.3.0
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@radix-ui/react-arrow@1.1.0':
resolution: {integrity: sha512-FmlW1rCg7hBpEBwFbjHwCW6AmWLQM6g/v0Sn8XbP9NvmSZ2San1FpQeyPtufzOMSIx7Y4dzjlHoifhp+7NkZhw==}
peerDependencies:
@@ -612,6 +631,19 @@ packages:
'@types/react-dom':
optional: true
'@radix-ui/react-collapsible@1.1.1':
resolution: {integrity: sha512-1///SnrfQHJEofLokyczERxQbWfCGQlQ2XsCZMucVs6it+lq9iw4vXy+uDn1edlb58cOZOWSldnfPAYcT4O/Yg==}
peerDependencies:
'@types/react': 18.3.5
'@types/react-dom': 18.3.0
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@radix-ui/react-collection@1.1.0':
resolution: {integrity: sha512-GZsZslMJEyo1VKm5L1ZJY8tGDxZNPAoUeQUIbKeJfoi7Q4kmig5AsgLMYYuyYbfjd8fBmFORAIwYAkXMnXZgZw==}
peerDependencies:
@@ -1136,6 +1168,14 @@ packages:
'@swc/helpers@0.5.5':
resolution: {integrity: sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==}
'@tabler/icons-react@3.21.0':
resolution: {integrity: sha512-Qq0GnZzzccbv/zuMyXAUUPlogNAqx9KsF8cr/ev3bxs+GMObqNEjXv1eZl9GFzxyQTS435siJNU8A1BaIYhX8g==}
peerDependencies:
react: '>= 16'
'@tabler/icons@3.21.0':
resolution: {integrity: sha512-5+GkkmWCr1wgMor5cOF1/YYflTQdc15y10FUikJ3HW8hDiFjfbuoAHJi17FT1vwsr1sA78rkJMn+fDoOOjnnPA==}
'@tailwindcss/typography@0.5.13':
resolution: {integrity: sha512-ADGcJ8dX21dVVHIwTRgzrcunY6YY9uSlAHHGVKvkA+vLc5qLwEszvKts40lx7z0qc4clpjclwLeK5rVCV2P/uw==}
peerDependencies:
@@ -3386,6 +3426,23 @@ snapshots:
'@types/react': 18.3.5
'@types/react-dom': 18.3.0
'@radix-ui/react-accordion@1.2.1(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
dependencies:
'@radix-ui/primitive': 1.1.0
'@radix-ui/react-collapsible': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@radix-ui/react-collection': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.5)(react@18.2.0)
'@radix-ui/react-context': 1.1.1(@types/react@18.3.5)(react@18.2.0)
'@radix-ui/react-direction': 1.1.0(@types/react@18.3.5)(react@18.2.0)
'@radix-ui/react-id': 1.1.0(@types/react@18.3.5)(react@18.2.0)
'@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.5)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
optionalDependencies:
'@types/react': 18.3.5
'@types/react-dom': 18.3.0
'@radix-ui/react-arrow@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
dependencies:
'@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
@@ -3420,6 +3477,22 @@ snapshots:
'@types/react': 18.3.5
'@types/react-dom': 18.3.0
'@radix-ui/react-collapsible@1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
dependencies:
'@radix-ui/primitive': 1.1.0
'@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.5)(react@18.2.0)
'@radix-ui/react-context': 1.1.1(@types/react@18.3.5)(react@18.2.0)
'@radix-ui/react-id': 1.1.0(@types/react@18.3.5)(react@18.2.0)
'@radix-ui/react-presence': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.5)(react@18.2.0)
'@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.5)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
optionalDependencies:
'@types/react': 18.3.5
'@types/react-dom': 18.3.0
'@radix-ui/react-collection@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
dependencies:
'@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.5)(react@18.2.0)
@@ -4108,6 +4181,13 @@ snapshots:
'@swc/counter': 0.1.3
tslib: 2.6.3
'@tabler/icons-react@3.21.0(react@18.2.0)':
dependencies:
'@tabler/icons': 3.21.0
react: 18.2.0
'@tabler/icons@3.21.0': {}
'@tailwindcss/typography@0.5.13(tailwindcss@3.4.7)':
dependencies:
lodash.castarray: 4.4.0