diff --git a/apps/dokploy/components/proprietary/enterprise-feature-gate.tsx b/apps/dokploy/components/proprietary/enterprise-feature-gate.tsx new file mode 100644 index 000000000..875813fcb --- /dev/null +++ b/apps/dokploy/components/proprietary/enterprise-feature-gate.tsx @@ -0,0 +1,114 @@ +"use client"; + +import { Loader2, Lock } from "lucide-react"; +import Link from "next/link"; +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { api } from "@/utils/api"; + +interface EnterpriseFeatureLockedProps { + /** Optional title override */ + title?: string; + /** Optional description override */ + description?: string; + /** Optional custom CTA label */ + ctaLabel?: string; + /** Optional CTA href (default: /dashboard/settings/license) */ + ctaHref?: string; + /** Compact variant (less padding, smaller icon) */ + compact?: boolean; +} + +/** + * Displays a locked state for enterprise features when the user has no valid license. + * Use standalone or via EnterpriseFeatureGate. + */ +export function EnterpriseFeatureLocked({ + title = "Enterprise feature", + description = "This feature is part of Dokploy Enterprise. Add a valid license to use it.", + ctaLabel = "Go to License", + ctaHref = "/dashboard/settings/license", + compact = false, +}: EnterpriseFeatureLockedProps) { + return ( + + + + + + + + {title} + + {description} + + + + + + + + {ctaLabel} + + + + + ); +} + +interface EnterpriseFeatureGateProps { + children: React.ReactNode; + /** Props for the locked state when license is invalid */ + lockedProps?: Omit; + /** Show loading spinner while checking license */ + fallback?: React.ReactNode; +} + +/** + * Renders children only when the instance has a valid enterprise license. + * Otherwise shows EnterpriseFeatureLocked. + */ +export function EnterpriseFeatureGate({ + children, + lockedProps, + fallback, +}: EnterpriseFeatureGateProps) { + const { data: haveValidLicense, isLoading } = + api.licenseKey.haveValidLicenseKey.useQuery(); + + if (isLoading) { + if (fallback) return <>{fallback}>; + return ( + + + + Checking license... + + + ); + } + + if (!haveValidLicense) { + return ; + } + + return <>{children}>; +} diff --git a/apps/dokploy/pages/dashboard/settings/sso.tsx b/apps/dokploy/pages/dashboard/settings/sso.tsx index 6085f7f0e..c0acedabb 100644 --- a/apps/dokploy/pages/dashboard/settings/sso.tsx +++ b/apps/dokploy/pages/dashboard/settings/sso.tsx @@ -4,6 +4,7 @@ import type { GetServerSidePropsContext } from "next"; import type { ReactElement } from "react"; import superjson from "superjson"; import { DashboardLayout } from "@/components/layouts/dashboard-layout"; +import { EnterpriseFeatureGate } from "@/components/proprietary/enterprise-feature-gate"; import { SSOSettings } from "@/components/proprietary/sso/sso-settings"; import { Card } from "@/components/ui/card"; import { appRouter } from "@/server/api/root"; @@ -16,7 +17,16 @@ const Page = () => { - + + + @@ -31,9 +41,7 @@ Page.getLayout = (page: ReactElement) => { return {page}; }; -export async function getServerSideProps( - ctx: GetServerSidePropsContext>, -) { +export async function getServerSideProps(ctx: GetServerSidePropsContext) { const { req, res } = ctx; const locale = await getLocale(req.cookies); if (IS_CLOUD) {