diff --git a/apps/docs/scripts/fix-openapi.mjs b/apps/docs/scripts/fix-openapi.mjs
index a95b1535..52706094 100644
--- a/apps/docs/scripts/fix-openapi.mjs
+++ b/apps/docs/scripts/fix-openapi.mjs
@@ -6,7 +6,15 @@ const openapiPath = join(process.cwd(), "public", "openapi.json");
console.log("Fixing OpenAPI schema...");
try {
- const openapi = JSON.parse(readFileSync(openapiPath, "utf8"));
+ let openapi = JSON.parse(readFileSync(openapiPath, "utf8"));
+
+ let unwrapped = false;
+ // If the spec is nested (e.g. result.data.json from a migrated/source API), use the inner spec
+ if (openapi.result?.data?.json && typeof openapi.result.data.json === "object") {
+ openapi = openapi.result.data.json;
+ unwrapped = true;
+ console.log("✓ Unwrapped nested OpenAPI spec (result.data.json)");
+ }
let fixed = 0;
let securityFixed = false;
@@ -87,7 +95,7 @@ try {
}
}
- if (fixed > 0 || securityFixed) {
+ if (unwrapped || fixed > 0 || securityFixed) {
writeFileSync(openapiPath, JSON.stringify(openapi, null, 2));
if (fixed > 0) console.log(`✓ Fixed ${fixed} empty response schemas`);
if (securityFixed) console.log("✓ Added x-api-key security scheme");
diff --git a/apps/website/app/partners/page.tsx b/apps/website/app/partners/page.tsx
new file mode 100644
index 00000000..3c657bef
--- /dev/null
+++ b/apps/website/app/partners/page.tsx
@@ -0,0 +1,152 @@
+import { Container } from "@/components/Container";
+import { PartnerForm } from "@/components/PartnerForm";
+import { Badge } from "@/components/ui/badge";
+import { Button } from "@/components/ui/button";
+import AnimatedGridPattern from "@/components/ui/animated-grid-pattern";
+import { Check } from "lucide-react";
+import Link from "next/link";
+import type { Metadata } from "next";
+
+export const metadata: Metadata = {
+ title: "Partners",
+ description:
+ "Join the Dokploy partner program. Agency plan, referral program, and reseller options.",
+};
+
+const PROGRAMS = [
+ {
+ title: "Agency Plan",
+ badge: "Available",
+ badgeVariant: "default" as const,
+ description:
+ "Premium licensing tier designed for agencies managing multiple clients.",
+ features: [
+ "White-label capabilities",
+ "Unlimited servers",
+ "Unlimited projects",
+ "Unlimited organizations",
+ ],
+ cta: "Get started",
+ href: "#get-started",
+ },
+ {
+ title: "Referral Program",
+ badge: "Available",
+ badgeVariant: "default" as const,
+ description:
+ "Earn 20% commission on every customer you refer to Dokploy.",
+ features: [
+ "Co-marketing opportunities",
+ "Partner dashboard",
+ "Unique referral links",
+ "20% of first-year revenue",
+ ],
+ cta: "Get started",
+ href: "#get-started",
+ },
+ {
+ title: "Reseller Program",
+ badge: "Coming Soon",
+ badgeVariant: "secondary" as const,
+ description:
+ "Sell Dokploy directly in your market with local presence and relationships.",
+ features: [
+ "Strategic market access",
+ "Cultural advantage",
+ "Leverage local expertise",
+ "Market expansion opportunity",
+ ],
+ cta: "Express interest",
+ href: "#get-started",
+ },
+];
+
+export default function PartnersPage() {
+ return (
+
+
+ {/* Hero */}
+
+
+
+
+ Partner with Dokploy
+
+
+ Join our partner program to unlock premium features, earn revenue
+ through referrals, and scale your agency operations.
+
+
+ Become a Partner
+
+
+
+
+
+ {/* Program cards */}
+
+
+
+ {PROGRAMS.map((program) => (
+
+
+ {program.badge}
+
+
+ {program.title}
+
+
+ {program.description}
+
+
+ {program.features.map((f) => (
+
+
+ {f}
+
+ ))}
+
+
+
+ {program.cta}
+
+
+
+ ))}
+
+
+
+
+ {/* Get Started / Form */}
+
+
+
+
+ Get Started
+
+
+ Join our partner program and start growing with Dokploy.
+
+
+
+
+
+
+ );
+}
diff --git a/apps/website/components/PartnerForm.tsx b/apps/website/components/PartnerForm.tsx
new file mode 100644
index 00000000..a70cc08c
--- /dev/null
+++ b/apps/website/components/PartnerForm.tsx
@@ -0,0 +1,239 @@
+"use client";
+
+import { trackGAEvent } from "@/components/analitycs";
+import { Button } from "@/components/ui/button";
+import { Input } from "@/components/ui/input";
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/components/ui/select";
+import { useState } from "react";
+
+const PROGRAM_OPTIONS = [
+ { value: "agency", label: "Agency Plan" },
+ { value: "referral", label: "Referral Program" },
+ { value: "reseller", label: "Reseller Program" },
+ { value: "all", label: "All Programs" },
+] as const;
+
+interface PartnerFormData {
+ firstName: string;
+ lastName: string;
+ email: string;
+ company: string;
+ programInterest: string;
+ message: string;
+}
+
+export function PartnerForm() {
+ const [isSubmitting, setIsSubmitting] = useState(false);
+ const [isSubmitted, setIsSubmitted] = useState(false);
+ const [formData, setFormData] = useState({
+ firstName: "",
+ lastName: "",
+ email: "",
+ company: "",
+ programInterest: "",
+ message: "",
+ });
+ const [errors, setErrors] = useState>({});
+
+ const validate = (): boolean => {
+ const newErrors: Record = {};
+ if (!formData.firstName.trim()) newErrors.firstName = "Required";
+ if (!formData.lastName.trim()) newErrors.lastName = "Required";
+ if (!formData.email.trim()) newErrors.email = "Required";
+ else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) {
+ newErrors.email = "Invalid email";
+ }
+ if (!formData.company.trim()) newErrors.company = "Required";
+ if (!formData.programInterest) newErrors.programInterest = "Required";
+ if (!formData.message.trim()) newErrors.message = "Required";
+ setErrors(newErrors);
+ return Object.keys(newErrors).length === 0;
+ };
+
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault();
+ if (!validate()) return;
+
+ setIsSubmitting(true);
+ try {
+ const programLabel =
+ PROGRAM_OPTIONS.find((o) => o.value === formData.programInterest)
+ ?.label ?? formData.programInterest;
+ const fullMessage = `Program Interest: ${programLabel}\n\n${formData.message}`;
+
+ const response = await fetch("/api/contact", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({
+ inquiryType: "sales",
+ firstName: formData.firstName,
+ lastName: formData.lastName,
+ email: formData.email,
+ company: formData.company,
+ message: fullMessage,
+ }),
+ });
+
+ if (response.ok) {
+ trackGAEvent({
+ action: "Partner Form Submitted",
+ category: "Partners",
+ label: formData.programInterest,
+ });
+ setIsSubmitted(true);
+ } else {
+ throw new Error("Failed to submit");
+ }
+ } catch {
+ setErrors({ message: "Something went wrong. Please try again." });
+ } finally {
+ setIsSubmitting(false);
+ }
+ };
+
+ if (isSubmitted) {
+ return (
+
+
+ Thank you for your interest
+
+
+ We've received your message and will get back to you soon.
+
+
+ );
+ }
+
+ return (
+
+ );
+}
diff --git a/apps/website/components/pricing-legacy.tsx b/apps/website/components/pricing-legacy.tsx
new file mode 100644
index 00000000..b167c255
--- /dev/null
+++ b/apps/website/components/pricing-legacy.tsx
@@ -0,0 +1,430 @@
+"use client";
+import clsx from "clsx";
+
+import { cn } from "@/lib/utils";
+import { IconInfoCircle } from "@tabler/icons-react";
+import {
+ ArrowRight,
+ MinusIcon,
+ PlusCircleIcon,
+ PlusIcon,
+ X,
+ XCircleIcon,
+} from "lucide-react";
+import Link from "next/link";
+import { useRouter } from "next/navigation";
+import { useState } from "react";
+import { Container } from "./Container";
+import { ContactFormModal } from "./ContactFormModal";
+import { Badge } from "./ui/badge";
+import AnimatedGradientText from "./ui/animated-gradient-text";
+import { Button, buttonVariants } from "./ui/button";
+import HeroVideoDialog from "./ui/hero-video-dialog";
+import { NumberInput } from "./ui/input";
+import { Tabs, TabsList, TabsTrigger } from "./ui/tabs";
+import {
+ Tooltip,
+ TooltipContent,
+ TooltipProvider,
+ TooltipTrigger,
+} from "./ui/tooltip";
+
+function SwirlyDoodle(props: React.ComponentPropsWithoutRef<"svg">) {
+ return (
+
+
+
+ );
+}
+
+function CheckIcon({
+ className,
+ ...props
+}: React.ComponentPropsWithoutRef<"svg">) {
+ return (
+
+
+
+
+ );
+}
+export const calculatePrice = (count: number, isAnnual = false) => {
+ if (isAnnual) {
+ if (count <= 1) return 45.9;
+ return 35.7 * count;
+ }
+ if (count <= 1) return 4.5;
+ return count * 3.5;
+};
+
+export function PricingLegacy() {
+ const router = useRouter();
+ const [isAnnual, setIsAnnual] = useState(false);
+ const [serverQuantity, setServerQuantity] = useState(1);
+ const featured = true;
+
+ const [openVideo, setOpenVideo] = useState(false);
+ const [openContactModal, setOpenContactModal] = useState(false);
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Simple Affordable
+ {" "}
+ Pricing.
+
+
+ Deploy Smarter and Scale Faster, Without Breaking the Bank
+
+
+
+
+
+
setIsAnnual(e === "annual")}
+ >
+
+ Monthly
+ Annual
+
+
+
+
+
+
+
+ Free
+
+ |
+
+ Open Source
+
+
+
+
+ Dokploy Open Source
+
+
+ Install and manage Dokploy UI on your own server.
+
+
+
+ {[
+ "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) => (
+
+
+ {feature}
+
+ ))}
+
+
+
+ Remote Servers Monitoring
+
+
+
+
+
+
+ Unlimited Servers
+
+
+ Start deploying{" "}
+
+
+
+
+
+
+ {isAnnual && (
+
+ Recommended 🚀
+
+ )}
+
+ {isAnnual ? (
+
+
+ $ {calculatePrice(serverQuantity, isAnnual).toFixed(2)}{" "}
+ USD
+
+ |
+
+ ${" "}
+ {(calculatePrice(serverQuantity, isAnnual) / 12).toFixed(
+ 2,
+ )}{" "}
+ / Month USD
+
+
+ ) : (
+
+ $ {calculatePrice(serverQuantity, isAnnual).toFixed(2)} USD
+
+ )}
+
+ Dokploy Plan
+
+
+ We manage the Dokploy UI infrastructure, we take care of it
+ for you.
+
+
+
+ {[
+ "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) => (
+
+
+ {feature}
+
+ ))}
+
+
+
+
+ No. of {serverQuantity} Servers (You bring the servers)
+
+
+
+ setOpenVideo(true)}>
+
+
+
+
+ setOpenVideo(false)}
+ className="flex size-4 cursor-pointer self-end text-muted-foreground transition-colors hover:text-primary"
+ />
+
+
+ We recommend you to watch the video to understand
+ the benefits of Dokploy Cloud
+
+
+
+
+
+
+
+
+
+
{
+ if (serverQuantity <= 1) return;
+
+ setServerQuantity(serverQuantity - 1);
+ }}
+ >
+
+
+
{
+ setServerQuantity(e.target.value as unknown as number);
+ }}
+ />
+
+ {
+ setServerQuantity(serverQuantity + 1);
+ }}
+ >
+
+
+
+
+
+
+
+
+
+
+
+ Enterprise
+
+
+ Premium ✨
+
+
+
+
+ Enterprise Support & Services
+
+
+ Custom solutions and dedicated support for your organization.
+
+
+
+ {[
+ "SLA Guarantees / Priority Support",
+ "Aditional Security & Governance",
+ "Custom Solutions",
+ "Private Labeling",
+ ].map((feature) => (
+
+
+ {feature}
+
+ ))}
+
+
+ setOpenContactModal(true)}
+ className="w-full"
+ >
+ Get in touch
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/apps/website/components/pricing.tsx b/apps/website/components/pricing.tsx
index 7e77c617..f373835f 100644
--- a/apps/website/components/pricing.tsx
+++ b/apps/website/components/pricing.tsx
@@ -1,33 +1,17 @@
"use client";
-import clsx from "clsx";
-import { cn } from "@/lib/utils";
-import { IconInfoCircle } from "@tabler/icons-react";
-import {
- ArrowRight,
- MinusIcon,
- PlusCircleIcon,
- PlusIcon,
- X,
- XCircleIcon,
-} from "lucide-react";
+import clsx from "clsx";
+import { Check } from "lucide-react";
import Link from "next/link";
-import { useRouter } from "next/navigation";
import { useState } from "react";
import { Container } from "./Container";
import { ContactFormModal } from "./ContactFormModal";
import { Badge } from "./ui/badge";
-import AnimatedGradientText from "./ui/animated-gradient-text";
import { Button, buttonVariants } from "./ui/button";
-import HeroVideoDialog from "./ui/hero-video-dialog";
-import { NumberInput } from "./ui/input";
import { Tabs, TabsList, TabsTrigger } from "./ui/tabs";
-import {
- Tooltip,
- TooltipContent,
- TooltipProvider,
- TooltipTrigger,
-} from "./ui/tooltip";
+import { PricingFeatureTable } from "./pricing/PricingFeatureTable";
+
+const CLOUD_APP_URL = "https://app.dokploy.com";
function SwirlyDoodle(props: React.ComponentPropsWithoutRef<"svg">) {
return (
@@ -46,66 +30,72 @@ function SwirlyDoodle(props: React.ComponentPropsWithoutRef<"svg">) {
);
}
-function CheckIcon({
- className,
- ...props
-}: React.ComponentPropsWithoutRef<"svg">) {
- return (
-
-
-
-
- );
-}
-export const calculatePrice = (count: number, isAnnual = false) => {
- if (isAnnual) {
- if (count <= 1) return 45.9;
- return 35.7 * count;
- }
- if (count <= 1) return 4.5;
- return count * 3.5;
-};
+const hobbyFeatures = [
+ "Unlimited Deployments",
+ "Unlimited Databases",
+ "Unlimited Applications",
+ "1 Server Included",
+ "1 Organization",
+ "1 User",
+ "2 Environments",
+ "1 Volume Backup per Application",
+ "1 Backup per Database",
+ "1 Scheduled Job per Application",
+ "Community Support (Discord)",
+];
+
+const startupFeatures = [
+ "All the features of Hobby, plus…",
+ "3 Servers Included",
+ "3 Organizations",
+ "Unlimited Users",
+ "Unlimited Environments",
+ "Unlimited Volume Backups",
+ "Unlimited Database Backups",
+ "Unlimited Scheduled Jobs",
+ "Basic RBAC (Admin, Developer)",
+ "2FA",
+ "Email and Chat Support",
+];
+
+const enterpriseFeatures = [
+ "All the features of Startup, plus…",
+ "Up to Unlimited Servers",
+ "Up to Unlimited Organizations",
+ "Fine-grained RBAC",
+ "Complete Hosting Flexibility",
+ "SSO / SAML (Azure, OKTA, etc)",
+ "Audit Logs",
+ "MSA/SLA",
+ "White Labeling",
+ "Priority Support and Services",
+];
export function Pricing() {
- const router = useRouter();
const [isAnnual, setIsAnnual] = useState(false);
- const [serverQuantity, setServerQuantity] = useState(1);
- const featured = true;
-
- const [openVideo, setOpenVideo] = useState(false);
const [openContactModal, setOpenContactModal] = useState(false);
+ const [openPartnerModal, setOpenPartnerModal] = useState(false);
+
+ const hobbyMonthlyPrice = 4.5;
+ const hobbyAnnualTotal = hobbyMonthlyPrice * 12 * 0.8; // 20% discount, total per year
+ const hobbyAnnualPerMonth = hobbyAnnualTotal / 12;
+ const startupBaseMonthly = 15;
+ const startupBaseAnnual = startupBaseMonthly * 12 * 0.8;
+
return (
-
+
-
+
@@ -125,306 +115,226 @@ export function Pricing() {
Pricing.
- Deploy Smarter and Scale Faster, Without Breaking the Bank
+ Infrastructure, we take care of it for you.
-
-
-
setIsAnnual(e === "annual")}
+ {/* Billing toggle */}
+
+ setIsAnnual(v === "annual")}
+ >
+
+
+ Yearly (20% discount)
+
+ Monthly
+
+
+
+
+
+ {/* Hobby, Startup, Enterprise - 3 column grid */}
+
+ {/* Hobby */}
+
-
- Monthly
- Annual
-
-
-
-
-
-
-
- Free
-
- |
-
- Open Source
+
Hobby
+
+ Everything an individual developer needs
+
+
+
+ $
+ {isAnnual
+ ? hobbyAnnualPerMonth.toFixed(2)
+ : hobbyMonthlyPrice.toFixed(2)}
+ /mo
+
+ {isAnnual ? (
+
+ ${hobbyAnnualTotal.toFixed(2)}/year per server
-
-
-
- Dokploy Open Source
-
-
- Install and manage Dokploy UI on your own server.
-
-
-
- {[
- "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) => (
-
-
- {feature}
-
- ))}
-
-
-
- Remote Servers Monitoring
-
-
-
-
-
-
- Unlimited Servers
-
-
- Start deploying{" "}
-
-
-
-
-
-
- {isAnnual && (
-
- Recommended 🚀
-
+ ) : (
+
+ per server (add as many servers as you'd like for $4.50/mo)
+
)}
+
+
+ {hobbyFeatures.map((f) => (
+
+
+ {f}
+
+ ))}
+
+
+
+ Get Started
+
+
+
+ {/* Startup */}
+
+
+ Recommended
+
+ Startup
+
+ Perfect for small to mid-size teams
+
+
+
+ Starting at $
+ {isAnnual
+ ? (startupBaseAnnual / 12).toFixed(2)
+ : startupBaseMonthly.toFixed(0)}
+ /mo
+
{isAnnual ? (
-
-
- $ {calculatePrice(serverQuantity, isAnnual).toFixed(2)}{" "}
- USD
-
- |
-
- ${" "}
- {(calculatePrice(serverQuantity, isAnnual) / 12).toFixed(
- 2,
- )}{" "}
- / Month USD
-
-
- ) : (
-
- $ {calculatePrice(serverQuantity, isAnnual).toFixed(2)} USD
+
+ ${startupBaseAnnual.toFixed(0)}/year
- )}
-
- Dokploy Plan
-
-
- We manage the Dokploy UI infrastructure, we take care of it
- for you.
+ ) : null}
+
+ Add more servers as you'd like for $4.50/mo
-
-
+
+ {startupFeatures.map((f) => (
+
+
+ {f}
+
+ ))}
+
+
+
- {[
- "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) => (
-
-
- {feature}
-
- ))}
-
-
-
-
- No. of {serverQuantity} Servers (You bring the servers)
-
-
-
- setOpenVideo(true)}>
-
-
-
-
- setOpenVideo(false)}
- className="flex size-4 cursor-pointer self-end text-muted-foreground transition-colors hover:text-primary"
- />
-
-
- We recommend you to watch the video to understand
- the benefits of Dokploy Cloud
-
-
-
-
-
-
-
-
-
- {
- if (serverQuantity <= 1) return;
-
- setServerQuantity(serverQuantity - 1);
- }}
- >
-
-
- {
- setServerQuantity(e.target.value as unknown as number);
- }}
- />
+ Get Started
+
+
+
-
{
- setServerQuantity(serverQuantity + 1);
- }}
- >
-
-
-
-
+ {/* Enterprise */}
+
+ Enterprise
+
+ For large organizations who want more control
+
+ {/* Cloud & Self Hosted options */}
+
+
+
Cloud
+
+ We host and manage everything for you
+
+
+
+
Self Hosted
+
+ Install on-prem or in your own cloud
+
-
-
-
+ {enterpriseFeatures.map((f) => (
+
+
+ {f}
+
+ ))}
+
+
+
setOpenContactModal(true)}
+ className="w-full"
>
-
-
- Enterprise
-
-
- Premium ✨
-
-
-
-
- Enterprise Support & Services
-
-
- Custom solutions and dedicated support for your organization.
-
+ Contact Sales
+
+
+
+
-
- {[
- "SLA Guarantees / Priority Support",
- "Aditional Security & Governance",
- "Custom Solutions",
- "Private Labeling",
- ].map((feature) => (
-
-
- {feature}
-
- ))}
-
-
- setOpenContactModal(true)}
- className="w-full"
- >
- Get in touch
-
-
-
+ {/* Agency - below the 3 main plans */}
+
+ Agency
+
+ Our Agency plan is uniquely tailored to the needs of agencies.
+ Please contact us below to learn more about this option, as well
+ as about becoming a certified Dokploy partner.{" "}
+
+ Learn more here
+
+
+
+ setOpenPartnerModal(true)}
+ className="w-full sm:w-auto"
+ variant="outline"
+ >
+ Contact The Partner Team
+
+
+
+
+ {/* Feature breakdown */}
+
+
+ Feature breakdown by plan
+
+
-
+
+
);
}
diff --git a/apps/website/components/pricing/PricingFeatureTable.tsx b/apps/website/components/pricing/PricingFeatureTable.tsx
new file mode 100644
index 00000000..0c8e4665
--- /dev/null
+++ b/apps/website/components/pricing/PricingFeatureTable.tsx
@@ -0,0 +1,76 @@
+"use client";
+
+import React from "react";
+import { Check } from "lucide-react";
+import { pricingFeatures, type FeatureValue } from "./pricing-data";
+
+function FeatureCell({ value }: { value: FeatureValue }) {
+ if (value === true) {
+ return (
+
+
+
+ );
+ }
+ if (value === false) {
+ return
—
;
+ }
+ return (
+
{value}
+ );
+}
+
+export function PricingFeatureTable() {
+ const grouped = pricingFeatures.reduce(
+ (acc, row) => {
+ if (!acc[row.category]) {
+ acc[row.category] = [];
+ }
+ acc[row.category].push(row);
+ return acc;
+ },
+ {} as Record
,
+ );
+
+ return (
+
+
+ {/* Header */}
+
+
+ Features
+
+
Hobby
+
Startup
+
Enterprise
+
+ {Object.entries(grouped).map(([category, rows]) => (
+
+
+ {category}
+
+ {rows.map((row) => (
+
+
+ {row.feature}
+
+
+
+
+
+
+
+
+
+
+
+ ))}
+
+ ))}
+
+
+ );
+}
diff --git a/apps/website/components/pricing/pricing-data.ts b/apps/website/components/pricing/pricing-data.ts
new file mode 100644
index 00000000..223de797
--- /dev/null
+++ b/apps/website/components/pricing/pricing-data.ts
@@ -0,0 +1,100 @@
+export type FeatureValue = true | false | string;
+
+export interface FeatureRow {
+ category: string;
+ feature: string;
+ hobby: FeatureValue;
+ startup: FeatureValue;
+ enterprise: FeatureValue;
+}
+
+export const pricingFeatures: FeatureRow[] = [
+ // Application and Compose Deployment
+ { category: "Application and Compose Deployment", feature: "Application Deployment", hobby: true, startup: true, enterprise: true },
+ { category: "Application and Compose Deployment", feature: "Multiple Build Types", hobby: true, startup: true, enterprise: true },
+ { category: "Application and Compose Deployment", feature: "Docker Compose Support (Stack & Compose)", hobby: true, startup: true, enterprise: true },
+ { category: "Application and Compose Deployment", feature: "Git Integration", hobby: true, startup: true, enterprise: true },
+ { category: "Application and Compose Deployment", feature: "Docker Registry Support", hobby: true, startup: true, enterprise: true },
+ { category: "Application and Compose Deployment", feature: "Auto Deploy via Webhooks", hobby: true, startup: true, enterprise: true },
+ { category: "Application and Compose Deployment", feature: "Preview Deployments", hobby: true, startup: true, enterprise: true },
+ { category: "Application and Compose Deployment", feature: "One-Click Templates", hobby: true, startup: true, enterprise: true },
+ { category: "Application and Compose Deployment", feature: "Watch Paths", hobby: true, startup: true, enterprise: true },
+ // Database Management
+ { category: "Database Management", feature: "Supported Databases", hobby: true, startup: true, enterprise: true },
+ { category: "Database Management", feature: "Database Management", hobby: true, startup: true, enterprise: true },
+ { category: "Database Management", feature: "Terminal Access", hobby: true, startup: true, enterprise: true },
+ { category: "Database Management", feature: "Database Backups", hobby: "1 backup per database", startup: "Unlimited Backups", enterprise: "Unlimited Backups" },
+ { category: "Database Management", feature: "Backup Logs", hobby: true, startup: true, enterprise: true },
+ { category: "Database Management", feature: "Custom Docker Images", hobby: true, startup: true, enterprise: true },
+ { category: "Database Management", feature: "Resource Limits", hobby: true, startup: true, enterprise: true },
+ // Infrastructure and Scaling
+ { category: "Infrastructure and Scaling", feature: "Multi-Server Support", hobby: true, startup: true, enterprise: true },
+ { category: "Infrastructure and Scaling", feature: "Docker Swarm Clusters", hobby: true, startup: true, enterprise: true },
+ { category: "Infrastructure and Scaling", feature: "Build Server Support", hobby: true, startup: true, enterprise: true },
+ { category: "Infrastructure and Scaling", feature: "Traefik Integration", hobby: true, startup: true, enterprise: true },
+ { category: "Infrastructure and Scaling", feature: "Custom Domain Management", hobby: true, startup: true, enterprise: true },
+ { category: "Infrastructure and Scaling", feature: "Auto SSL/TLS", hobby: true, startup: true, enterprise: true },
+ { category: "Infrastructure and Scaling", feature: "Zero Downtime Deployments", hobby: true, startup: true, enterprise: true },
+ { category: "Infrastructure and Scaling", feature: "Registry-based Rollbacks", hobby: true, startup: true, enterprise: true },
+ { category: "Infrastructure and Scaling", feature: "Resource Limits", hobby: true, startup: true, enterprise: true },
+ { category: "Infrastructure and Scaling", feature: "Volume Management", hobby: true, startup: true, enterprise: true },
+ { category: "Infrastructure and Scaling", feature: "Volume Backups", hobby: "1 backup per volume", startup: "Unlimited Backups", enterprise: "Unlimited Backups" },
+ // Monitoring and Logging
+ { category: "Monitoring and Logging", feature: "Real-time Monitoring", hobby: true, startup: true, enterprise: true },
+ { category: "Monitoring and Logging", feature: "Application Logs", hobby: true, startup: true, enterprise: true },
+ { category: "Monitoring and Logging", feature: "Deployment Logs", hobby: true, startup: true, enterprise: true },
+ { category: "Monitoring and Logging", feature: "Deployment History", hobby: true, startup: true, enterprise: true },
+ { category: "Monitoring and Logging", feature: "Queue Management", hobby: true, startup: true, enterprise: true },
+ // Automation and Scheduling
+ { category: "Automation and Scheduling", feature: "Scheduled Jobs", hobby: "1 job per server or container", startup: "Unlimited Jobs", enterprise: "Unlimited Jobs" },
+ { category: "Automation and Scheduling", feature: "Docker Cleanup Jobs", hobby: true, startup: true, enterprise: true },
+ { category: "Automation and Scheduling", feature: "Custom Scripts", hobby: true, startup: true, enterprise: true },
+ { category: "Automation and Scheduling", feature: "CI/CD Integration", hobby: true, startup: true, enterprise: true },
+ { category: "Automation and Scheduling", feature: "Run Commands", hobby: true, startup: true, enterprise: true },
+ // Notifications and Alerts
+ { category: "Notifications and Alerts", feature: "Slack Notifications", hobby: true, startup: true, enterprise: true },
+ { category: "Notifications and Alerts", feature: "Discord Notifications", hobby: true, startup: true, enterprise: true },
+ { category: "Notifications and Alerts", feature: "Telegram Notifications", hobby: true, startup: true, enterprise: true },
+ { category: "Notifications and Alerts", feature: "Email Notifications", hobby: true, startup: true, enterprise: true },
+ { category: "Notifications and Alerts", feature: "Webhook Notifications", hobby: true, startup: true, enterprise: true },
+ { category: "Notifications and Alerts", feature: "Lark Notifications", hobby: true, startup: true, enterprise: true },
+ { category: "Notifications and Alerts", feature: "Gotify Integration", hobby: true, startup: true, enterprise: true },
+ { category: "Notifications and Alerts", feature: "Ntfy Integration", hobby: true, startup: true, enterprise: true },
+ { category: "Notifications and Alerts", feature: "Event Triggers", hobby: true, startup: true, enterprise: true },
+ // Security
+ { category: "Security", feature: "Two-Factor Authentication", hobby: true, startup: true, enterprise: true },
+ { category: "Security", feature: "API Key Authentication", hobby: true, startup: true, enterprise: true },
+ { category: "Security", feature: "SSH Key Management", hobby: true, startup: true, enterprise: true },
+ { category: "Security", feature: "Certificate Management", hobby: true, startup: true, enterprise: true },
+ { category: "Security", feature: "Security Audits", hobby: true, startup: true, enterprise: true },
+ { category: "Security", feature: "UFW Firewall Guidance", hobby: true, startup: true, enterprise: true },
+ // User and Organization Management
+ { category: "User and Organization Management", feature: "User Management", hobby: "1 user", startup: "Unlimited", enterprise: "Unlimited" },
+ { category: "User and Organization Management", feature: "Organizations", hobby: "1 Organizations", startup: "3 Organizations", enterprise: "10+ Organizations" },
+ { category: "User and Organization Management", feature: "Projects", hobby: true, startup: true, enterprise: true },
+ { category: "User and Organization Management", feature: "Environment", hobby: "2 Environments", startup: "Unlimited", enterprise: "Unlimited" },
+ { category: "User and Organization Management", feature: "Role-based Permissions", hobby: "NA", startup: "Admin, Developer", enterprise: "Admin, Developer" },
+ { category: "User and Organization Management", feature: "Social Login", hobby: "Google, Github, Gitlab", startup: "Google, Github, Gitlab", enterprise: "SSO (Okta, Azure, and more)" },
+ // Developer Tools
+ { category: "Developer Tools", feature: "REST API", hobby: true, startup: true, enterprise: true },
+ { category: "Developer Tools", feature: "CLI Tool", hobby: true, startup: true, enterprise: true },
+ { category: "Developer Tools", feature: "Environment Variables", hobby: true, startup: true, enterprise: true },
+ { category: "Developer Tools", feature: "Container Terminal", hobby: true, startup: true, enterprise: true },
+ { category: "Developer Tools", feature: "Keyboard Shortcuts", hobby: true, startup: true, enterprise: true },
+ { category: "Developer Tools", feature: "Swagger Documentation", hobby: true, startup: true, enterprise: true },
+ // Advanced Configuration
+ { category: "Advanced Configuration", feature: "Swarm Settings", hobby: true, startup: true, enterprise: true },
+ { category: "Advanced Configuration", feature: "Health Checks", hobby: true, startup: true, enterprise: true },
+ { category: "Advanced Configuration", feature: "Network Configuration", hobby: true, startup: true, enterprise: true },
+ { category: "Advanced Configuration", feature: "Container Labels", hobby: true, startup: true, enterprise: true },
+ { category: "Advanced Configuration", feature: "Traefik File Editor", hobby: true, startup: true, enterprise: true },
+ { category: "Advanced Configuration", feature: "Port Configuration", hobby: true, startup: true, enterprise: true },
+ { category: "Advanced Configuration", feature: "Security Headers", hobby: true, startup: true, enterprise: true },
+ { category: "Advanced Configuration", feature: "Redirects", hobby: true, startup: true, enterprise: true },
+ // Support and Services
+ { category: "Support and Services", feature: "Community Support", hobby: true, startup: true, enterprise: true },
+ { category: "Support and Services", feature: "Email and Chat Support", hobby: false, startup: true, enterprise: true },
+ { category: "Support and Services", feature: "Phone & Video Support", hobby: false, startup: false, enterprise: true },
+ { category: "Support and Services", feature: "SLA", hobby: "Online T&C's", startup: "Online T&C's", enterprise: true },
+ { category: "Support and Services", feature: "MSA", hobby: "Online T&C's", startup: "Online T&C's", enterprise: true },
+];
diff --git a/apps/website/components/ui/badge.tsx b/apps/website/components/ui/badge.tsx
index f38976c0..835d311e 100644
--- a/apps/website/components/ui/badge.tsx
+++ b/apps/website/components/ui/badge.tsx
@@ -1,3 +1,4 @@
+"use client";
import { type VariantProps, cva } from "class-variance-authority";
import type * as React from "react";