This commit is contained in:
Philipinho
2025-07-29 14:28:40 -07:00
parent a3d058042f
commit 6db93ef0c7
5 changed files with 66 additions and 24 deletions

View File

@ -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 = (
<Link
onMouseEnter={prefetchHandler}
onMouseEnter={!isDisabled ? prefetchHandler : undefined}
className={classes.link}
data-active={active.startsWith(item.path) || undefined}
data-disabled={isDisabled || undefined}
key={item.label}
to={item.path}
onClick={() => {
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",
}}
>
<item.icon className={classes.linkIcon} stroke={2} />
<span>{t(item.label)}</span>
</Link>
);
if (isDisabled) {
return (
<Tooltip
key={item.label}
label={t("Available in enterprise edition")}
position="right"
withArrow
>
{linkElement}
</Tooltip>
);
}
return linkElement;
})}
</div>
);

View File

@ -11,7 +11,7 @@ export default function OssDetails() {
withTableBorder
>
<Table.Caption>
To unlock enterprise features like SSO, contact sales@docmost.com.
To unlock enterprise features like SSO, MFA, Resolve comments, contact sales@docmost.com.
</Table.Caption>
<Table.Tbody>
<Table.Tr>

View File

@ -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() {
</div>
{!isMfaEnabled ? (
<Button
variant="default"
onClick={() => setSetupModalOpen(true)}
style={{ whiteSpace: "nowrap" }}
<Tooltip
label={t("Available in enterprise edition")}
disabled={canUseMfa}
>
{t("Add 2FA method")}
</Button>
<Button
disabled={!canUseMfa}
variant="default"
onClick={() => setSetupModalOpen(true)}
style={{ whiteSpace: "nowrap" }}
>
{t("Add 2FA method")}
</Button>
</Tooltip>
) : (
<Group gap="sm" wrap="nowrap">
<Button

View File

@ -319,7 +319,7 @@ function ImportFormatSelection({ spaceId, onClose }: ImportFormatSelection) {
>
{(props) => (
<Tooltip
label="Available in enterprise edition"
label={t("Available in enterprise edition")}
disabled={canUseConfluence}
>
<Button

View File

@ -1,15 +1,6 @@
import React from "react";
import { isCloud } from "@/lib/config";
import { useLicense } from "@/ee/hooks/use-license";
import { MfaSettings } from "@/ee/mfa";
export function AccountMfaSection() {
const { hasLicenseKey } = useLicense();
const showMfa = isCloud() || hasLicenseKey;
if (!showMfa) {
return null;
}
return <MfaSettings />;
}