mirror of
https://github.com/Dokploy/website
synced 2025-06-26 18:16:01 +00:00
feat: add new design
This commit is contained in:
178
apps/website/app/[locale]/_changelog/page.tsx
Normal file
178
apps/website/app/[locale]/_changelog/page.tsx
Normal file
File diff suppressed because one or more lines are too long
@@ -4,6 +4,8 @@ import { Hero } from "@/components/Hero";
|
||||
import { PrimaryFeatures } from "@/components/PrimaryFeatures";
|
||||
import { Testimonials } from "@/components/Testimonials";
|
||||
import { FeaturesSectionDemo } from "@/components/features";
|
||||
import { Pricing } from "@/components/pricing";
|
||||
import { RippleDemo } from "@/components/sponsors";
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
@@ -13,7 +15,12 @@ export default function Home() {
|
||||
<FeaturesSectionDemo />
|
||||
<PrimaryFeatures />
|
||||
<Testimonials />
|
||||
<div className="w-full relative">
|
||||
<Pricing />
|
||||
</div>
|
||||
<Faqs />
|
||||
|
||||
<RippleDemo />
|
||||
<CallToAction />
|
||||
</main>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
import { Pricing } from "@/components/pricing";
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div className="w-full">
|
||||
<Pricing />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -12,6 +12,34 @@ const faqs = [
|
||||
question: "faq.q1",
|
||||
answer: "faq.a1",
|
||||
},
|
||||
{
|
||||
question: "faq.q11",
|
||||
answer: "faq.a11",
|
||||
},
|
||||
{
|
||||
question: "faq.q12",
|
||||
answer: "faq.a12",
|
||||
},
|
||||
{
|
||||
question: "faq.q13",
|
||||
answer: "faq.a13",
|
||||
},
|
||||
{
|
||||
question: "faq.q14",
|
||||
answer: "faq.a14",
|
||||
},
|
||||
{
|
||||
question: "faq.q15",
|
||||
answer: "faq.a15",
|
||||
},
|
||||
{
|
||||
question: "faq.q17",
|
||||
answer: "faq.a17",
|
||||
},
|
||||
{
|
||||
question: "faq.q18",
|
||||
answer: "faq.a18",
|
||||
},
|
||||
{
|
||||
question: "faq.q2",
|
||||
answer: "faq.a2",
|
||||
@@ -36,6 +64,10 @@ const faqs = [
|
||||
question: "faq.q8",
|
||||
answer: "faq.a8",
|
||||
},
|
||||
{
|
||||
question: "faq.q16",
|
||||
answer: "faq.a16",
|
||||
},
|
||||
{
|
||||
question: "faq.q9",
|
||||
answer: "faq.a9",
|
||||
@@ -74,7 +106,9 @@ export function Faqs() {
|
||||
>
|
||||
{faqs.map((column, columnIndex) => (
|
||||
<AccordionItem value={`${columnIndex}`} key={columnIndex}>
|
||||
<AccordionTrigger>{t(column.question)}</AccordionTrigger>
|
||||
<AccordionTrigger className="text-left">
|
||||
{t(column.question)}
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>{t(column.answer)}</AccordionContent>
|
||||
</AccordionItem>
|
||||
))}
|
||||
|
||||
@@ -67,10 +67,14 @@ export function Footer() {
|
||||
aria-label="Dokploy on Twitter"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
className="h-6 w-6 fill-muted-foreground group-hover:fill-muted-foreground/70"
|
||||
stroke="currentColor"
|
||||
fill="currentColor"
|
||||
strokeWidth="0"
|
||||
viewBox="0 0 512 512"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="h-5 w-5 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" />
|
||||
<path d="M389.2 48h70.6L305.6 224.2 487 464H345L233.7 318.6 106.5 464H35.8L200.7 275.5 26.8 48H172.4L272.9 180.9 389.2 48zM364.4 421.8h39.1L151.1 88h-42L364.4 421.8z" />
|
||||
</svg>
|
||||
</Link>
|
||||
<Link
|
||||
|
||||
@@ -3,13 +3,14 @@
|
||||
import { Link } from "@/i18n/routing";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Popover, Transition } from "@headlessui/react";
|
||||
import { HeartIcon } from "lucide-react";
|
||||
import { ChevronRight, HeartIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { Fragment, type JSX, type SVGProps } from "react";
|
||||
import { Container } from "./Container";
|
||||
import { NavLink } from "./NavLink";
|
||||
import { trackGAEvent } from "./analitycs";
|
||||
import { Logo } from "./shared/Logo";
|
||||
import AnimatedGradientText from "./ui/animated-gradient-text";
|
||||
import { Button, buttonVariants } from "./ui/button";
|
||||
|
||||
function MobileNavLink({
|
||||
@@ -119,7 +120,7 @@ function MobileNavigation() {
|
||||
as="div"
|
||||
className="absolute inset-x-0 top-full mt-4 flex origin-top flex-col rounded-2xl border border-border bg-background p-4 text-lg tracking-tight text-primary shadow-xl ring-1 ring-border/5"
|
||||
>
|
||||
<MobileNavLink href="/pricing">Pricing</MobileNavLink>
|
||||
<MobileNavLink href="#pricing">Pricing</MobileNavLink>
|
||||
<MobileNavLink href="/#faqs">{t("navigation.faqs")}</MobileNavLink>
|
||||
<MobileNavLink href={linkT("docs.intro")} target="_blank">
|
||||
{t("navigation.docs")}
|
||||
@@ -144,7 +145,7 @@ export function Header() {
|
||||
<Logo className="h-10 w-auto" />
|
||||
</Link>
|
||||
<div className="hidden md:flex md:gap-x-6">
|
||||
<NavLink href="/pricing">{t("navigation.pricing")}</NavLink>
|
||||
<NavLink href="#pricing">{t("navigation.pricing")}</NavLink>
|
||||
<NavLink href="/#faqs">{t("navigation.faqs")}</NavLink>
|
||||
<NavLink href={linkT("docs.intro")} target="_blank">
|
||||
{t("navigation.docs")}
|
||||
@@ -152,7 +153,20 @@ export function Header() {
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-x-2 md:gap-x-5">
|
||||
<Link
|
||||
<Link href="https://x.com/getdokploy" target="_blank">
|
||||
<svg
|
||||
stroke="currentColor"
|
||||
fill="currentColor"
|
||||
strokeWidth="0"
|
||||
viewBox="0 0 512 512"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="h-5 w-5 fill-muted-foreground group-hover:fill-muted-foreground/70 hover:fill-muted-foreground/80"
|
||||
>
|
||||
<path d="M389.2 48h70.6L305.6 224.2 487 464H345L233.7 318.6 106.5 464H35.8L200.7 275.5 26.8 48H172.4L272.9 180.9 389.2 48zM364.4 421.8h39.1L151.1 88h-42L364.4 421.8z" />
|
||||
</svg>
|
||||
</Link>
|
||||
|
||||
{/* <Link
|
||||
className={buttonVariants({
|
||||
variant: "outline",
|
||||
className: " flex items-center gap-2 !rounded-full",
|
||||
@@ -164,14 +178,18 @@ export function Header() {
|
||||
{t("navigation.support")}{" "}
|
||||
</span>
|
||||
<HeartIcon className="animate-heartbeat size-4 fill-red-600 text-red-500 " />
|
||||
</Link>
|
||||
</Link> */}
|
||||
|
||||
<Button className="rounded-full" asChild>
|
||||
<Link
|
||||
href="https://app.dokploy.com"
|
||||
aria-label="Dokploy on GitHub"
|
||||
href="https://app.dokploy.com/register"
|
||||
aria-label="Sign In Dokploy Cloud"
|
||||
target="_blank"
|
||||
>
|
||||
{t("navigation.dashboard")}
|
||||
<div className="group flex-row relative mx-auto flex max-w-fit items-center justify-center rounded-2xl text-sm font-medium w-full">
|
||||
<span>{t("navigation.dashboard")}</span>
|
||||
<ChevronRight className="ml-1 size-3 transition-transform duration-300 ease-in-out group-hover:translate-x-0.5" />
|
||||
</div>
|
||||
</Link>
|
||||
</Button>
|
||||
<div className="-mr-1 md:hidden">
|
||||
|
||||
@@ -158,7 +158,7 @@ export function PrimaryFeatures() {
|
||||
{features.map((feature, index) => (
|
||||
<Tab.Panel key={`panel-${index}`}>
|
||||
<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" />
|
||||
<div className="absolute -inset-x-4 bottom-[-4.25rem] top-[-6.5rem] bg-card/60 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 mb-10">
|
||||
{t(feature.description)}
|
||||
</p>
|
||||
@@ -174,7 +174,7 @@ export function PrimaryFeatures() {
|
||||
bounce: 0.2,
|
||||
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] "
|
||||
className="mt-10 h-[24rem] w-[45rem] overflow-hidden rounded-xl border-b shadow-xl sm:w-auto lg:mt-0 lg:h-[40rem] "
|
||||
>
|
||||
<div className="relative">
|
||||
<Safari
|
||||
|
||||
@@ -188,11 +188,11 @@ export function Testimonials() {
|
||||
aria-label="What our customers are saying"
|
||||
className=" py-20 sm:py-32"
|
||||
>
|
||||
<div className="mx-auto max-w-2xl md:text-center">
|
||||
<h2 className="font-display text-3xl tracking-tight sm:text-4xl">
|
||||
<div className="mx-auto max-w-2xl md:text-center px-4">
|
||||
<h2 className="font-display text-3xl tracking-tight sm:text-4xl text-center">
|
||||
Why Developers Love Dokploy
|
||||
</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">
|
||||
Think we’re bragging? Hear from the devs who once doubted too—until
|
||||
Dokploy made their lives (and deployments) surprisingly easier.
|
||||
</p>
|
||||
|
||||
@@ -98,7 +98,7 @@ export function FeaturesSectionDemo() {
|
||||
flexible database management—all with Dokploy’s 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">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 relative z-10 py-10 max-w-7xl mx-auto mt-10 max-sm:p-0 max-sm:mx-0 max-sm:w-full">
|
||||
{features.map((feature, index) => (
|
||||
<Feature key={feature.title} {...feature} index={index} />
|
||||
))}
|
||||
|
||||
File diff suppressed because one or more lines are too long
29
apps/website/components/sponsors.tsx
Normal file
29
apps/website/components/sponsors.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import { PlusCircleIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { Button } from "./ui/button";
|
||||
import Ripple from "./ui/ripple";
|
||||
|
||||
export const RippleDemo = () => {
|
||||
const t = useTranslations("HomePage");
|
||||
return (
|
||||
<div className="mt-20 flex flex-col justify-center gap-y-10 w-full">
|
||||
<div className="flex flex-col justify-start gap-4 px-4">
|
||||
<h1 className="mx-auto max-w-2xl font-display text-3xl font-medium tracking-tight text-primary sm:text-5xl text-center">
|
||||
{t("hero.sponsors.title")}
|
||||
</h1>
|
||||
<p className="mx-auto max-w-2xl text-lg tracking-tight text-muted-foreground text-center">
|
||||
{t("hero.sponsors.description")}
|
||||
</p>
|
||||
</div>
|
||||
<div className="relative flex h-[500px] w-full flex-col items-center justify-center overflow-hidden rounded-lg border bg-background md:shadow-xl">
|
||||
<p className="z-10 whitespace-pre-wrap text-center text-5xl font-medium tracking-tighter text-white">
|
||||
<Button variant="secondary" className="rounded-full p-0 m-0">
|
||||
<PlusCircleIcon className="size-10 text-muted-foreground hover:text-primary transition-colors" />
|
||||
</Button>
|
||||
{/* <PlusCircleIcon className="size-10 " /> */}
|
||||
</p>
|
||||
<Ripple />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
50
apps/website/components/ui/avatar.tsx
Normal file
50
apps/website/components/ui/avatar.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
"use client";
|
||||
|
||||
import * as AvatarPrimitive from "@radix-ui/react-avatar";
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const Avatar = React.forwardRef<
|
||||
React.ElementRef<typeof AvatarPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<AvatarPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
Avatar.displayName = AvatarPrimitive.Root.displayName;
|
||||
|
||||
const AvatarImage = React.forwardRef<
|
||||
React.ElementRef<typeof AvatarPrimitive.Image>,
|
||||
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<AvatarPrimitive.Image
|
||||
ref={ref}
|
||||
className={cn("aspect-square h-full w-full", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
AvatarImage.displayName = AvatarPrimitive.Image.displayName;
|
||||
|
||||
const AvatarFallback = React.forwardRef<
|
||||
React.ElementRef<typeof AvatarPrimitive.Fallback>,
|
||||
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<AvatarPrimitive.Fallback
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex h-full w-full items-center justify-center rounded-full bg-muted",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;
|
||||
|
||||
export { Avatar, AvatarImage, AvatarFallback };
|
||||
172
apps/website/components/ui/ripple.tsx
Normal file
172
apps/website/components/ui/ripple.tsx
Normal file
@@ -0,0 +1,172 @@
|
||||
"use client";
|
||||
import React, { type CSSProperties } from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "./avatar";
|
||||
|
||||
interface RippleProps {
|
||||
mainCircleSize?: number;
|
||||
mainCircleOpacity?: number;
|
||||
numCircles?: number;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const Ripple = React.memo(function Ripple({
|
||||
mainCircleSize = 210,
|
||||
mainCircleOpacity = 0.24,
|
||||
numCircles = 8,
|
||||
className,
|
||||
}: RippleProps) {
|
||||
const avatarsFirstRing = [
|
||||
"https://github.com/shadcn.png", // URLs de los avatares del primer aro
|
||||
"https://github.com/shadcn.png",
|
||||
"https://github.com/shadcn.png",
|
||||
"https://github.com/shadcn.png",
|
||||
"https://github.com/shadcn.png",
|
||||
];
|
||||
const avatarsSecondRing = [
|
||||
"https://github.com/shadcn.png", // URLs de los avatares del segundo aro
|
||||
"https://github.com/shadcn.png",
|
||||
"https://github.com/shadcn.png",
|
||||
"https://github.com/shadcn.png",
|
||||
"https://github.com/shadcn.png",
|
||||
"https://github.com/shadcn.png",
|
||||
];
|
||||
|
||||
const avatarsThirdRing = [
|
||||
"https://github.com/shadcn.png", // URLs de los avatares del tercer aro
|
||||
"https://github.com/shadcn.png",
|
||||
"https://github.com/shadcn.png",
|
||||
"https://github.com/shadcn.png",
|
||||
"https://github.com/shadcn.png",
|
||||
"https://github.com/shadcn.png",
|
||||
"https://github.com/shadcn.png",
|
||||
"https://github.com/shadcn.png",
|
||||
];
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
// "pointer-events-none select-none absolute inset-0 [mask-image:linear-gradient(to_bottom,white,transparent)]",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<div>
|
||||
{Array.from({ length: numCircles }, (_, i) => {
|
||||
const size = mainCircleSize + i * 70;
|
||||
const opacity = mainCircleOpacity - i * 0.03;
|
||||
const animationDelay = `${i * 0.06}s`;
|
||||
const borderStyle = i === numCircles - 1 ? "dashed" : "solid";
|
||||
const borderOpacity = 5 + i * 5;
|
||||
|
||||
return (
|
||||
<div
|
||||
key={i}
|
||||
className={`absolute animate-ripple rounded-full bg-foreground/25 shadow-xl border [--i:${i}]`}
|
||||
style={{
|
||||
width: `${size}px`,
|
||||
height: `${size}px`,
|
||||
opacity,
|
||||
animationDelay,
|
||||
borderStyle,
|
||||
borderWidth: "1px",
|
||||
borderColor: `hsl(var(--foreground), ${borderOpacity / 100})`,
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
transform: "translate(-50%, -50%) scale(1)",
|
||||
}}
|
||||
>
|
||||
{i === 0 && (
|
||||
<div className="relative w-full h-full flex justify-center items-center">
|
||||
{avatarsFirstRing.map((src, index) => {
|
||||
const angle = (360 / avatarsFirstRing.length) * index;
|
||||
const radius = mainCircleSize / 2;
|
||||
const x = radius * Math.cos((angle * Math.PI) / 180);
|
||||
const y = radius * Math.sin((angle * Math.PI) / 180);
|
||||
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className="absolute"
|
||||
style={{
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
transform: `translate(${x}px, ${y}px) translate(-50%, -50%)`,
|
||||
}}
|
||||
>
|
||||
<Avatar>
|
||||
<AvatarImage src={src} />
|
||||
<AvatarFallback>CN</AvatarFallback>
|
||||
</Avatar>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{i === 1 && (
|
||||
<div className="relative w-full h-full flex justify-center items-center">
|
||||
{avatarsSecondRing.map((src, index) => {
|
||||
const angle = (360 / avatarsSecondRing.length) * index;
|
||||
const radius = mainCircleSize / 2 + 70; // Radio mayor para el segundo aro
|
||||
const x = radius * Math.cos((angle * Math.PI) / 180);
|
||||
const y = radius * Math.sin((angle * Math.PI) / 180);
|
||||
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className="absolute"
|
||||
style={{
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
transform: `translate(${x}px, ${y}px) translate(-50%, -50%)`,
|
||||
}}
|
||||
>
|
||||
<Avatar>
|
||||
<AvatarImage src={src} />
|
||||
<AvatarFallback>CN</AvatarFallback>
|
||||
</Avatar>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{i === 3 && (
|
||||
<div className="relative w-full h-full flex justify-center items-center">
|
||||
{avatarsThirdRing.map((src, index) => {
|
||||
const angle = (360 / avatarsThirdRing.length) * index;
|
||||
const radius = mainCircleSize / 2 + 140; // Radio mayor para el tercer aro
|
||||
const x = radius * Math.cos((angle * Math.PI) / 180);
|
||||
const y = radius * Math.sin((angle * Math.PI) / 180);
|
||||
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className="absolute"
|
||||
style={{
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
transform: `translate(${x}px, ${y}px) translate(-50%, -50%)`,
|
||||
}}
|
||||
>
|
||||
<Avatar>
|
||||
<AvatarImage src={src} />
|
||||
<AvatarFallback>CN</AvatarFallback>
|
||||
</Avatar>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
Ripple.displayName = "Ripple";
|
||||
|
||||
export default Ripple;
|
||||
48
apps/website/components/ui/scroll-area.tsx
Normal file
48
apps/website/components/ui/scroll-area.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
"use client";
|
||||
|
||||
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const ScrollArea = React.forwardRef<
|
||||
React.ElementRef<typeof ScrollAreaPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<ScrollAreaPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn("relative overflow-hidden", className)}
|
||||
{...props}
|
||||
>
|
||||
<ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]">
|
||||
{children}
|
||||
</ScrollAreaPrimitive.Viewport>
|
||||
<ScrollBar />
|
||||
<ScrollAreaPrimitive.Corner />
|
||||
</ScrollAreaPrimitive.Root>
|
||||
));
|
||||
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
|
||||
|
||||
const ScrollBar = React.forwardRef<
|
||||
React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
|
||||
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
|
||||
>(({ className, orientation = "vertical", ...props }, ref) => (
|
||||
<ScrollAreaPrimitive.ScrollAreaScrollbar
|
||||
ref={ref}
|
||||
orientation={orientation}
|
||||
className={cn(
|
||||
"flex touch-none select-none transition-colors",
|
||||
orientation === "vertical" &&
|
||||
"h-full w-2.5 border-l border-l-transparent p-[1px]",
|
||||
orientation === "horizontal" &&
|
||||
"h-2.5 flex-col border-t border-t-transparent p-[1px]",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border" />
|
||||
</ScrollAreaPrimitive.ScrollAreaScrollbar>
|
||||
));
|
||||
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;
|
||||
|
||||
export { ScrollArea, ScrollBar };
|
||||
@@ -6,7 +6,7 @@
|
||||
"docs": "Docs",
|
||||
"pricing": "Pricing",
|
||||
"support": "Support",
|
||||
"dashboard": "Dashboard",
|
||||
"dashboard": "Sign In",
|
||||
"discord": "Discord",
|
||||
"i18nButtonPlaceholder": "Language",
|
||||
"i18nEn": "English",
|
||||
@@ -91,7 +91,23 @@
|
||||
"q9": "What types of applications can I deploy with Dokploy?",
|
||||
"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 and backups 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.",
|
||||
"q11": "How does Dokploy's Open Source plan work?",
|
||||
"a11": "You can host Dokploy UI on your own infrastructure and you will be responsible for the maintenance and updates.",
|
||||
"q12": "Do I need to provide my own server for the managed plan?",
|
||||
"a12": "Yes, in the managed plan, you provide your own server eg(Hetzner, Hostinger, AWS, ETC.) VPS, and we manage the Dokploy UI infrastructure for you.",
|
||||
"q13": "What happens if I need more than one server?",
|
||||
"a13": "The first server costs $4.50/month, if you buy more than one it will be $3.50/month per server.",
|
||||
"q14": "Is there a limit on the number of deployments?",
|
||||
"a14": "No, there is no limit on the number of deployments in any of the plans.",
|
||||
"q15": "What happens if I exceed my purchased server limit?",
|
||||
"a15": "The most recently added servers will be deactivated. You won't be able to create services on inactive servers until they are reactivated.",
|
||||
"q16": "Do you offer a refunds?",
|
||||
"a16": "We do not offer refunds. However, you can cancel your subscription at any time. Feel free to try our open-source version for free before making a purchase.",
|
||||
"q17": "What kind of support do you offer?",
|
||||
"a17": "We offer community support for the open source version and priority support for paid plans (Via Discord or Email at support@dokploy.com).",
|
||||
"q18": "What's the catch on the Paid Plan?",
|
||||
"a18": "Nothing, you link your server(VPS) to your account and you can deploy unlimited applications, databases, and users and you get unlimited updates, deployments, backups and more."
|
||||
},
|
||||
"footer": {
|
||||
"copyright": "Copyright © {year} Dokploy. All rights reserved."
|
||||
@@ -126,13 +142,10 @@
|
||||
},
|
||||
"features": {
|
||||
"f1": "Complete Flexibility: Install Dokploy UI on your own infrastructure",
|
||||
"f2": "Unlimited Deployments",
|
||||
"f3": "Self-hosted Infrastructure",
|
||||
"f4": "Community Support",
|
||||
"f5": "Access to Core Features",
|
||||
"f6": "Dokploy Integration",
|
||||
"f7": "Basic Backups",
|
||||
"f8": "Access to All Updates",
|
||||
"f2": "Self-hosted Infrastructure",
|
||||
"f3": "Community Support",
|
||||
"f4": "Access to Core Features",
|
||||
"f5": "Access to All Updates",
|
||||
"f9": "Unlimited Servers"
|
||||
},
|
||||
"go": "Installation"
|
||||
@@ -146,8 +159,12 @@
|
||||
"servers": "{serverQuantity} Servers (You bring the servers)",
|
||||
"features": {
|
||||
"f1": "Managed Hosting: No need to manage your own servers",
|
||||
"f2": "Priority Support",
|
||||
"f3": "Future-Proof Features"
|
||||
"f2": "Unlimited Deployments",
|
||||
"f3": "Unlimited Databases",
|
||||
"f4": "Unlimited Applications",
|
||||
"f5": "Unlimited Users",
|
||||
"f6": "Priority Support",
|
||||
"f7": "New Updates"
|
||||
},
|
||||
"go": "Subscribe"
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
"@headlessui/react": "^1.7.17",
|
||||
"@headlessui/tailwindcss": "^0.2.0",
|
||||
"@radix-ui/react-accordion": "^1.2.1",
|
||||
"@radix-ui/react-avatar": "^1.1.1",
|
||||
"@radix-ui/react-scroll-area": "^1.2.0",
|
||||
"@radix-ui/react-select": "^2.0.0",
|
||||
"@radix-ui/react-slot": "^1.0.2",
|
||||
"@radix-ui/react-switch": "^1.0.3",
|
||||
|
||||
@@ -124,6 +124,14 @@ const config = {
|
||||
backgroundPosition: "var(--bg-size) 0",
|
||||
},
|
||||
},
|
||||
ripple: {
|
||||
"0%, 100%": {
|
||||
transform: "translate(-50%, -50%) scale(1)",
|
||||
},
|
||||
"50%": {
|
||||
transform: "translate(-50%, -50%) scale(0.9)",
|
||||
},
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
"accordion-down": "accordion-down 0.2s ease-out",
|
||||
@@ -132,6 +140,7 @@ const config = {
|
||||
marquee: "marquee var(--duration) linear infinite",
|
||||
"marquee-vertical": "marquee-vertical var(--duration) linear infinite",
|
||||
gradient: "gradient 8s linear infinite",
|
||||
ripple: "ripple var(--duration,2s) ease calc(var(--i, 0)*.2s) infinite",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
61
pnpm-lock.yaml
generated
61
pnpm-lock.yaml
generated
@@ -91,6 +91,12 @@ importers:
|
||||
'@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-avatar':
|
||||
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)
|
||||
'@radix-ui/react-scroll-area':
|
||||
specifier: ^1.2.0
|
||||
version: 1.2.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-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)
|
||||
@@ -618,6 +624,19 @@ packages:
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-avatar@1.1.1':
|
||||
resolution: {integrity: sha512-eoOtThOmxeoizxpX6RiEsQZ2wj5r4+zoeqAwO0cBaFQGjJwIH3dIX0OCxNrCyrrdxG+vBweMETh3VziQG7c1kw==}
|
||||
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-collapsible@1.1.0':
|
||||
resolution: {integrity: sha512-zQY7Epa8sTL0mq4ajSJpjgn2YmCgyrG7RsQgLp3C0LQVkG7+Tf6Pv1CeNWZLyqMjhdPkBa5Lx7wYBeSu7uCSTA==}
|
||||
peerDependencies:
|
||||
@@ -968,6 +987,19 @@ packages:
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-scroll-area@1.2.0':
|
||||
resolution: {integrity: sha512-q2jMBdsJ9zB7QG6ngQNzNwlvxLQqONyL58QbEGwuyRZZb/ARQwk3uQVbCF7GvQVOtV6EU/pDxAw3zRzJZI3rpQ==}
|
||||
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-select@2.1.1':
|
||||
resolution: {integrity: sha512-8iRDfyLtzxlprOo9IicnzvpsO1wNCkuwzzCM+Z5Rb5tNOpCdMvcc2AkzX0Fz+Tz9v6NJ5B/7EEgyZveo4FBRfQ==}
|
||||
peerDependencies:
|
||||
@@ -3461,6 +3493,18 @@ snapshots:
|
||||
'@types/react': 18.3.5
|
||||
'@types/react-dom': 18.3.0
|
||||
|
||||
'@radix-ui/react-avatar@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/react-context': 1.1.1(@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-callback-ref': 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-collapsible@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||
dependencies:
|
||||
'@radix-ui/primitive': 1.1.0
|
||||
@@ -3932,6 +3976,23 @@ snapshots:
|
||||
'@types/react': 18.3.5
|
||||
'@types/react-dom': 18.3.0
|
||||
|
||||
'@radix-ui/react-scroll-area@1.2.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/number': 1.1.0
|
||||
'@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-direction': 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-callback-ref': 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-select@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)':
|
||||
dependencies:
|
||||
'@radix-ui/number': 1.1.0
|
||||
|
||||
Reference in New Issue
Block a user