mirror of
https://github.com/Dokploy/website.git
synced 2026-06-15 20:25:25 +02:00
feat: update pricing component layout and features
- Introduced a new state for enterprise deployment options (cloud/self-hosted). - Redesigned the pricing section to include a "Hobby" plan with detailed features. - Improved layout with a grid system for better responsiveness. - Updated pricing display logic for clarity on annual vs monthly rates.
This commit is contained in:
@@ -88,7 +88,9 @@ export function Pricing() {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [isAnnual, setIsAnnual] = useState(false);
|
const [isAnnual, setIsAnnual] = useState(false);
|
||||||
const [serverQuantity, setServerQuantity] = useState(1);
|
const [serverQuantity, setServerQuantity] = useState(1);
|
||||||
const featured = true;
|
const [enterpriseDeployment, setEnterpriseDeployment] = useState<
|
||||||
|
"cloud" | "self-hosted"
|
||||||
|
>("cloud");
|
||||||
|
|
||||||
const [openVideo, setOpenVideo] = useState(false);
|
const [openVideo, setOpenVideo] = useState(false);
|
||||||
const [openContactModal, setOpenContactModal] = useState(false);
|
const [openContactModal, setOpenContactModal] = useState(false);
|
||||||
@@ -141,281 +143,245 @@ export function Pricing() {
|
|||||||
<TabsTrigger value="annual">Annual</TabsTrigger>
|
<TabsTrigger value="annual">Annual</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
<div className="mx-auto flex max-w-4xl flex-col gap-8">
|
<div className="mx-auto w-full max-w-6xl">
|
||||||
<div className="flex gap-4 max-sm:flex-wrap max-sm:justify-center sm:flex-row">
|
<div className="grid w-full grid-cols-1 gap-4 lg:grid-cols-3">
|
||||||
<section
|
{/* Hobby */}
|
||||||
className={clsx(
|
<section className="flex flex-col rounded-3xl border-2 border-dashed border-muted bg-black p-6">
|
||||||
"flex max-w-sm flex-col rounded-3xl border-2 border-dashed border-muted px-4",
|
<h3 className="text-center text-xl font-semibold tracking-tight text-white">
|
||||||
featured
|
Hobby
|
||||||
? "order-first border bg-black py-8 lg:order-none"
|
</h3>
|
||||||
: "lg:py-8",
|
<ul className="mt-6 flex flex-col gap-y-2 text-sm text-muted-foreground">
|
||||||
)}
|
{[
|
||||||
>
|
"Managed Hosting: No need to manage your own servers",
|
||||||
<div className="flex flex-row items-center gap-2">
|
"Unlimited Deployments",
|
||||||
<p className=" text-2xl font-semibold tracking-tight text-primary ">
|
"Unlimited Databases",
|
||||||
Free
|
"Unlimited Applications",
|
||||||
</p>
|
"Up to 1 user",
|
||||||
|
|
"1 environment",
|
||||||
<p className=" text-base font-semibold tracking-tight text-muted-foreground">
|
"1 organization",
|
||||||
Open Source
|
].map((feature) => (
|
||||||
</p>
|
<li key={feature} className="flex">
|
||||||
</div>
|
<CheckIcon className="text-muted-foreground" />
|
||||||
|
<span className="ml-2">{feature}</span>
|
||||||
<h3 className="mt-5 text-lg font-medium text-white">
|
</li>
|
||||||
Dokploy Open Source
|
))}
|
||||||
</h3>
|
<li className="flex">
|
||||||
<p
|
<XCircleIcon className="size-5 self-center text-destructive" />
|
||||||
className={clsx(
|
<span className="ml-3 text-destructive">
|
||||||
"text-sm",
|
Remote Servers Monitoring
|
||||||
featured ? "text-white" : "text-slate-400",
|
</span>
|
||||||
)}
|
|
||||||
>
|
|
||||||
Install and manage Dokploy UI on your own server.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<ul
|
|
||||||
role="list"
|
|
||||||
className={clsx(
|
|
||||||
" mt-4 flex flex-col gap-y-2 text-sm",
|
|
||||||
featured ? "text-white" : "text-slate-200",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{[
|
|
||||||
"Complete Flexibility: Install Dokploy UI on your own infrastructure",
|
|
||||||
"Self-hosted Infrastructure",
|
|
||||||
"Community Support",
|
|
||||||
"Access to Core Features",
|
|
||||||
"Access to All Updates",
|
|
||||||
"Unlimited Servers",
|
|
||||||
].map((feature) => (
|
|
||||||
<li key={feature} className="flex text-muted-foreground">
|
|
||||||
<CheckIcon />
|
|
||||||
<span className="ml-2">{feature}</span>
|
|
||||||
</li>
|
</li>
|
||||||
))}
|
<li className="flex">
|
||||||
<li className="flex text-muted-foreground">
|
<XCircleIcon className="size-5 self-center text-destructive" />
|
||||||
<XCircleIcon className="size-5 self-center text-destructive" />
|
<span className="ml-3 text-destructive">Email Support</span>
|
||||||
<span className="ml-3 text-destructive">
|
|
||||||
Remote Servers Monitoring
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div className="mt-4 flex flex-col gap-2">
|
|
||||||
<div className="flex flex-col items-center justify-center gap-2">
|
|
||||||
<span className="text-sm text-muted-foreground">
|
|
||||||
Unlimited Servers
|
|
||||||
</span>
|
|
||||||
<Link
|
|
||||||
href="https://docs.dokploy.com/docs/core/installation#docker"
|
|
||||||
target="_blank"
|
|
||||||
className="flex items-start text-sm text-primary"
|
|
||||||
>
|
|
||||||
Start deploying{" "}
|
|
||||||
<ArrowRight className="ml-2 size-4 self-center" />
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<section
|
|
||||||
className={clsx(
|
|
||||||
"flex max-w-sm flex-col rounded-3xl border-2 border-dashed px-4",
|
|
||||||
featured
|
|
||||||
? "order-first border bg-black py-8 lg:order-none"
|
|
||||||
: "lg:py-8",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{isAnnual && (
|
|
||||||
<div className="mb-4 flex flex-row items-center gap-2">
|
|
||||||
<Badge>Recommended 🚀</Badge>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{isAnnual ? (
|
|
||||||
<div className="flex flex-row items-center gap-2">
|
|
||||||
<p className=" text-2xl font-semibold tracking-tight text-primary ">
|
|
||||||
$ {calculatePrice(serverQuantity, isAnnual).toFixed(2)}{" "}
|
|
||||||
USD
|
|
||||||
</p>
|
|
||||||
|
|
|
||||||
<p className=" text-base font-semibold tracking-tight text-muted-foreground">
|
|
||||||
${" "}
|
|
||||||
{(calculatePrice(serverQuantity, isAnnual) / 12).toFixed(
|
|
||||||
2,
|
|
||||||
)}{" "}
|
|
||||||
/ Month USD
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<p className=" text-2xl font-semibold tracking-tight text-primary">
|
|
||||||
$ {calculatePrice(serverQuantity, isAnnual).toFixed(2)} USD
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
<h3 className="mt-5 text-lg font-medium text-white">
|
|
||||||
Dokploy Plan
|
|
||||||
</h3>
|
|
||||||
<p
|
|
||||||
className={clsx(
|
|
||||||
"text-sm",
|
|
||||||
featured ? "text-white" : "text-slate-400",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
We manage the Dokploy UI infrastructure, we take care of it
|
|
||||||
for you.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<ul
|
|
||||||
role="list"
|
|
||||||
className={clsx(
|
|
||||||
" mt-4 flex flex-col gap-y-2 text-sm",
|
|
||||||
featured ? "text-white" : "text-slate-200",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{[
|
|
||||||
"Managed Hosting: No need to manage your own servers",
|
|
||||||
"Unlimited Deployments",
|
|
||||||
"Unlimited Databases",
|
|
||||||
"Unlimited Applications",
|
|
||||||
"Unlimited Users",
|
|
||||||
"Remote Servers Monitoring",
|
|
||||||
"Priority Support",
|
|
||||||
].map((feature, index) => (
|
|
||||||
<li
|
|
||||||
key={`${feature}-${index}`}
|
|
||||||
className="flex text-muted-foreground"
|
|
||||||
>
|
|
||||||
<CheckIcon />
|
|
||||||
<span className="ml-2">{feature}</span>
|
|
||||||
</li>
|
</li>
|
||||||
))}
|
</ul>
|
||||||
</ul>
|
|
||||||
<div className="mt-4 flex flex-col gap-2">
|
|
||||||
<div className="flex items-center justify-center gap-2">
|
|
||||||
<span className="text-sm text-muted-foreground">
|
|
||||||
No. of {serverQuantity} Servers (You bring the servers)
|
|
||||||
</span>
|
|
||||||
<TooltipProvider>
|
|
||||||
<Tooltip open={openVideo}>
|
|
||||||
<TooltipTrigger onClick={() => setOpenVideo(true)}>
|
|
||||||
<IconInfoCircle className="size-5 text-muted-foreground transition-colors hover:text-primary " />
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent className=" z-[200] w-[400px] rounded-lg text-center font-semibold text-white">
|
|
||||||
<div className="mb-2 flex w-full justify-end self-end text-muted-foreground transition-colors hover:text-primary">
|
|
||||||
<X
|
|
||||||
onClick={() => setOpenVideo(false)}
|
|
||||||
className="flex size-4 cursor-pointer self-end text-muted-foreground transition-colors hover:text-primary"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<p className="mb-2 text-left text-primary">
|
|
||||||
We recommend you to watch the video to understand
|
|
||||||
the benefits of Dokploy Cloud
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<HeroVideoDialog
|
<div className="mt-auto pt-8">
|
||||||
className="z-20 block w-full max-w-md rounded-xl"
|
<div className="rounded-2xl bg-muted/10 p-4 text-center">
|
||||||
animationStyle="top-in-bottom-out"
|
<p className="text-lg font-semibold text-primary">
|
||||||
videoSrc="https://www.youtube.com/embed/x2s_Y5ON-ms?si=i6gntgMmyPDLuPih"
|
$
|
||||||
thumbnailSrc="https://dokploy.com/banner.png"
|
{calculatePrice(1, isAnnual).toFixed(isAnnual ? 1 : 2)}
|
||||||
thumbnailAlt="Hero Video"
|
{isAnnual ? "/yr" : "/mo"} per server
|
||||||
/>
|
</p>
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
</TooltipProvider>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<Button
|
|
||||||
disabled={serverQuantity <= 1}
|
|
||||||
variant="outline"
|
|
||||||
onClick={() => {
|
|
||||||
if (serverQuantity <= 1) return;
|
|
||||||
|
|
||||||
setServerQuantity(serverQuantity - 1);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<MinusIcon className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
<NumberInput
|
|
||||||
value={serverQuantity}
|
|
||||||
onChange={(e) => {
|
|
||||||
setServerQuantity(e.target.value as unknown as number);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
onClick={() => {
|
|
||||||
setServerQuantity(serverQuantity + 1);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<PlusIcon className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className={cn(
|
|
||||||
"justify-between",
|
|
||||||
"mt-4 flex flex-row items-center gap-2",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<div className="w-full justify-end">
|
|
||||||
<Link
|
|
||||||
href="https://app.dokploy.com/register"
|
|
||||||
target="_blank"
|
|
||||||
className={buttonVariants({
|
|
||||||
className: "w-full",
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
Subscribe
|
|
||||||
</Link>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</section>
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-center">
|
|
||||||
<section
|
|
||||||
className={clsx(
|
|
||||||
"flex w-full max-w-4xl flex-col rounded-3xl border-2 border-dashed border-muted px-4 py-4",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<div className="flex flex-row items-center justify-start gap-3 w-fit">
|
|
||||||
<p className="text-xl font-semibold tracking-tight text-primary">
|
|
||||||
Enterprise
|
|
||||||
</p>
|
|
||||||
<AnimatedGradientText className="text-xs">
|
|
||||||
Premium ✨
|
|
||||||
</AnimatedGradientText>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h3 className="mt-3 text-base font-medium text-white">
|
{/* Startup */}
|
||||||
Enterprise Support & Services
|
<section className="flex flex-col rounded-3xl border-2 border-dashed border-muted bg-black p-6">
|
||||||
|
<h3 className="text-center text-xl font-semibold tracking-tight text-white">
|
||||||
|
Startup
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-sm text-muted-foreground">
|
<ul className="mt-6 flex flex-col gap-y-2 text-sm text-muted-foreground">
|
||||||
Custom solutions and dedicated support for your organization.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<ul className="mt-3 grid grid-cols-2 gap-y-1 text-sm text-slate-200">
|
|
||||||
{[
|
{[
|
||||||
"SLA Guarantees / Priority Support",
|
"All the features of Hobby, plus...",
|
||||||
"Aditional Security & Governance",
|
"Audit Logs",
|
||||||
"Custom Solutions",
|
"Multiple Environments (3 included)",
|
||||||
"Private Labeling",
|
"Unlimited users",
|
||||||
|
"3 servers included",
|
||||||
|
"3 organizations included",
|
||||||
].map((feature) => (
|
].map((feature) => (
|
||||||
<li key={feature} className="flex text-muted-foreground">
|
<li key={feature} className="flex">
|
||||||
<CheckIcon />
|
<CheckIcon className="text-muted-foreground" />
|
||||||
<span className="ml-2">{feature}</span>
|
<span className="ml-2">{feature}</span>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
<div className="mt-4 flex flex-col gap-2">
|
|
||||||
|
<div className="mt-8 flex flex-col gap-4">
|
||||||
|
<div className="flex items-center justify-center gap-2">
|
||||||
|
<span className="text-sm text-muted-foreground">
|
||||||
|
No. of {serverQuantity} Servers (You bring the servers)
|
||||||
|
</span>
|
||||||
|
<TooltipProvider>
|
||||||
|
<Tooltip open={openVideo}>
|
||||||
|
<TooltipTrigger onClick={() => setOpenVideo(true)}>
|
||||||
|
<IconInfoCircle className="size-5 text-muted-foreground transition-colors hover:text-primary " />
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent className=" z-[200] w-[400px] rounded-lg text-center font-semibold text-white">
|
||||||
|
<div className="mb-2 flex w-full justify-end self-end text-muted-foreground transition-colors hover:text-primary">
|
||||||
|
<X
|
||||||
|
onClick={() => setOpenVideo(false)}
|
||||||
|
className="flex size-4 cursor-pointer self-end text-muted-foreground transition-colors hover:text-primary"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<p className="mb-2 text-left text-primary">
|
||||||
|
We recommend you to watch the video to understand
|
||||||
|
the benefits of Dokploy Cloud
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<HeroVideoDialog
|
||||||
|
className="z-20 block w-full max-w-md rounded-xl"
|
||||||
|
animationStyle="top-in-bottom-out"
|
||||||
|
videoSrc="https://www.youtube.com/embed/x2s_Y5ON-ms?si=i6gntgMmyPDLuPih"
|
||||||
|
thumbnailSrc="https://dokploy.com/banner.png"
|
||||||
|
thumbnailAlt="Hero Video"
|
||||||
|
/>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</TooltipProvider>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center justify-center space-x-2">
|
||||||
|
<Button
|
||||||
|
disabled={serverQuantity <= 1}
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => {
|
||||||
|
if (serverQuantity <= 1) return;
|
||||||
|
setServerQuantity(serverQuantity - 1);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MinusIcon className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
<NumberInput
|
||||||
|
value={serverQuantity}
|
||||||
|
onChange={(e) => {
|
||||||
|
setServerQuantity(
|
||||||
|
e.target.value as unknown as number,
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => setServerQuantity(serverQuantity + 1)}
|
||||||
|
>
|
||||||
|
<PlusIcon className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="rounded-2xl bg-muted/10 p-4 text-center">
|
||||||
|
<p className="text-lg font-semibold text-primary">
|
||||||
|
Starting @{" "}
|
||||||
|
{isAnnual
|
||||||
|
? `$${(15 * 12).toFixed(0)}/yr`
|
||||||
|
: "$15/mo"}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Link
|
||||||
|
href="https://app.dokploy.com/register"
|
||||||
|
target="_blank"
|
||||||
|
className={buttonVariants({ className: "w-full" })}
|
||||||
|
>
|
||||||
|
Subscribe
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Enterprise */}
|
||||||
|
<section className="flex flex-col rounded-3xl border-2 border-dashed border-muted bg-black p-6">
|
||||||
|
<div className="flex flex-col items-center gap-2">
|
||||||
|
<h3 className="text-center text-xl font-semibold tracking-tight text-white">
|
||||||
|
Enterprise
|
||||||
|
</h3>
|
||||||
|
<div className="mt-2">
|
||||||
|
<Tabs
|
||||||
|
value={enterpriseDeployment}
|
||||||
|
onValueChange={(v) =>
|
||||||
|
setEnterpriseDeployment(
|
||||||
|
v === "self-hosted" ? "self-hosted" : "cloud",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<TabsList className="grid w-[260px] grid-cols-2 h-auto">
|
||||||
|
<TabsTrigger value="cloud">Cloud</TabsTrigger>
|
||||||
|
<TabsTrigger value="self-hosted">
|
||||||
|
Self-Hosted
|
||||||
|
</TabsTrigger>
|
||||||
|
</TabsList>
|
||||||
|
</Tabs>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul className="mt-6 flex flex-col gap-y-2 text-sm text-muted-foreground">
|
||||||
|
<li className="flex">
|
||||||
|
<CheckIcon className="text-muted-foreground" />
|
||||||
|
<span className="ml-2">
|
||||||
|
All the features of Business, plus...
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
{[
|
||||||
|
"SSO",
|
||||||
|
"Role based access controls",
|
||||||
|
"Unlimited Environments",
|
||||||
|
"Priority Support",
|
||||||
|
"MSA/SLA",
|
||||||
|
"White Labeling",
|
||||||
|
].map((feature) => (
|
||||||
|
<li key={feature} className="flex">
|
||||||
|
<CheckIcon className="text-muted-foreground" />
|
||||||
|
<span className="ml-2">{feature}</span>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
{enterpriseDeployment === "self-hosted" && (
|
||||||
|
<li className="flex">
|
||||||
|
<CheckIcon className="text-muted-foreground" />
|
||||||
|
<span className="ml-2">
|
||||||
|
Self-hosted deployment support
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div className="mt-auto pt-8">
|
||||||
|
<div className="rounded-2xl bg-muted/10 p-4 text-center">
|
||||||
|
<p className="text-lg font-semibold text-primary">
|
||||||
|
Contact Sales
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => setOpenContactModal(true)}
|
onClick={() => setOpenContactModal(true)}
|
||||||
className="w-full"
|
className="mt-4 w-full"
|
||||||
>
|
>
|
||||||
Get in touch
|
Contact Sales
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Agency */}
|
||||||
|
<section className="mt-6 rounded-3xl border-2 border-dashed border-muted bg-black p-6">
|
||||||
|
<div className="flex flex-col items-center gap-3 text-center">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<p className="text-xl font-semibold tracking-tight text-white">
|
||||||
|
Agency
|
||||||
|
</p>
|
||||||
|
<AnimatedGradientText className="text-xs">
|
||||||
|
Partner ✨
|
||||||
|
</AnimatedGradientText>
|
||||||
|
</div>
|
||||||
|
<p className="max-w-3xl text-sm text-muted-foreground">
|
||||||
|
Our Agency plan is uniquely tailored to the needs of agencies.
|
||||||
|
Please contact us to learn more about this option as well as
|
||||||
|
becoming a certified Dokploy partner.
|
||||||
|
</p>
|
||||||
|
<Button onClick={() => setOpenContactModal(true)}>
|
||||||
|
Contact The Partner Team
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user