diff --git a/apps/client/src/components/settings/settings-sidebar.tsx b/apps/client/src/components/settings/settings-sidebar.tsx index 79ee511d..f506d746 100644 --- a/apps/client/src/components/settings/settings-sidebar.tsx +++ b/apps/client/src/components/settings/settings-sidebar.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from "react"; -import { Group, Text, ScrollArea, ActionIcon } from "@mantine/core"; +import { Group, Text, ScrollArea, ActionIcon, Tooltip } from "@mantine/core"; import { IconUser, IconSettings, @@ -42,6 +42,7 @@ interface DataItem { isEnterprise?: boolean; isAdmin?: boolean; isSelfhosted?: boolean; + showDisabledInNonEE?: boolean; } interface DataGroup { @@ -84,6 +85,7 @@ const groupedData: DataGroup[] = [ isCloud: true, isEnterprise: true, isAdmin: true, + showDisabledInNonEE: true, }, { label: "Groups", icon: IconUsersGroup, path: "/settings/groups" }, { label: "Spaces", icon: IconSpaces, path: "/settings/spaces" }, @@ -117,6 +119,12 @@ export default function SettingsSidebar() { }, [location.pathname]); const canShowItem = (item: DataItem) => { + // For items with showDisabledInCommunity, always show them but in disabled state + if (item.showDisabledInNonEE && item.isEnterprise) { + // Check admin permission regardless of license + return item.isAdmin ? isAdmin : true; + } + if (item.isCloud && item.isEnterprise) { if (!(isCloud() || workspace?.hasLicenseKey)) return false; return item.isAdmin ? isAdmin : true; @@ -141,6 +149,13 @@ export default function SettingsSidebar() { return true; }; + const isItemDisabled = (item: DataItem) => { + if (item.showDisabledInNonEE && item.isEnterprise) { + return !(isCloud() || workspace?.hasLicenseKey); + } + return false; + }; + const menuItems = groupedData.map((group) => { if (group.heading === "System" && (!isAdmin || isCloud())) { return null; @@ -185,23 +200,48 @@ export default function SettingsSidebar() { break; } - return ( + const isDisabled = isItemDisabled(item); + const linkElement = ( { + to={isDisabled ? "#" : item.path} + onClick={(e) => { + if (isDisabled) { + e.preventDefault(); + return; + } if (mobileSidebarOpened) { toggleMobileSidebar(); } }} + style={{ + opacity: isDisabled ? 0.5 : 1, + cursor: isDisabled ? "not-allowed" : "pointer", + }} > {t(item.label)} ); + + if (isDisabled) { + return ( + + {linkElement} + + ); + } + + return linkElement; })} ); diff --git a/apps/client/src/ee/licence/components/oss-details.tsx b/apps/client/src/ee/licence/components/oss-details.tsx index 4dbd84a7..2856c5f8 100644 --- a/apps/client/src/ee/licence/components/oss-details.tsx +++ b/apps/client/src/ee/licence/components/oss-details.tsx @@ -11,7 +11,7 @@ export default function OssDetails() { withTableBorder > - To unlock enterprise features like SSO, contact sales@docmost.com. + To unlock enterprise features like SSO, MFA, Resolve comments, contact sales@docmost.com. diff --git a/apps/client/src/ee/mfa/components/mfa-settings.tsx b/apps/client/src/ee/mfa/components/mfa-settings.tsx index beab11a0..bf849f3e 100644 --- a/apps/client/src/ee/mfa/components/mfa-settings.tsx +++ b/apps/client/src/ee/mfa/components/mfa-settings.tsx @@ -1,5 +1,5 @@ import React, { useState } from "react"; -import { Group, Text, Button } from "@mantine/core"; +import { Group, Text, Button, Tooltip } from "@mantine/core"; import { useQuery, useQueryClient } from "@tanstack/react-query"; import { notifications } from "@mantine/notifications"; import { useTranslation } from "react-i18next"; @@ -7,6 +7,8 @@ import { getMfaStatus } from "@/ee/mfa"; import { MfaSetupModal } from "@/ee/mfa"; import { MfaDisableModal } from "@/ee/mfa"; import { MfaBackupCodesModal } from "@/ee/mfa"; +import { isCloud } from "@/lib/config.ts"; +import useLicense from "@/ee/hooks/use-license.tsx"; export function MfaSettings() { const { t } = useTranslation(); @@ -14,16 +16,19 @@ export function MfaSettings() { const [setupModalOpen, setSetupModalOpen] = useState(false); const [disableModalOpen, setDisableModalOpen] = useState(false); const [backupCodesModalOpen, setBackupCodesModalOpen] = useState(false); + const { hasLicenseKey } = useLicense(); const { data: mfaStatus, isLoading } = useQuery({ queryKey: ["mfa-status"], queryFn: getMfaStatus, }); - if (isLoading) { + if (isLoading || !mfaStatus) { return null; } + const canUseMfa = isCloud() || hasLicenseKey; + // Check if MFA is truly enabled const isMfaEnabled = mfaStatus?.isEnabled === true; @@ -61,13 +66,19 @@ export function MfaSettings() { {!isMfaEnabled ? ( - + + ) : (