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 React, { useEffect, useState } from "react";
import { Group, Text, ScrollArea, ActionIcon } from "@mantine/core"; import { Group, Text, ScrollArea, ActionIcon, Tooltip } from "@mantine/core";
import { import {
IconUser, IconUser,
IconSettings, IconSettings,
@ -42,6 +42,7 @@ interface DataItem {
isEnterprise?: boolean; isEnterprise?: boolean;
isAdmin?: boolean; isAdmin?: boolean;
isSelfhosted?: boolean; isSelfhosted?: boolean;
showDisabledInNonEE?: boolean;
} }
interface DataGroup { interface DataGroup {
@ -84,6 +85,7 @@ const groupedData: DataGroup[] = [
isCloud: true, isCloud: true,
isEnterprise: true, isEnterprise: true,
isAdmin: true, isAdmin: true,
showDisabledInNonEE: true,
}, },
{ label: "Groups", icon: IconUsersGroup, path: "/settings/groups" }, { label: "Groups", icon: IconUsersGroup, path: "/settings/groups" },
{ label: "Spaces", icon: IconSpaces, path: "/settings/spaces" }, { label: "Spaces", icon: IconSpaces, path: "/settings/spaces" },
@ -117,6 +119,12 @@ export default function SettingsSidebar() {
}, [location.pathname]); }, [location.pathname]);
const canShowItem = (item: DataItem) => { 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 (item.isCloud && item.isEnterprise) {
if (!(isCloud() || workspace?.hasLicenseKey)) return false; if (!(isCloud() || workspace?.hasLicenseKey)) return false;
return item.isAdmin ? isAdmin : true; return item.isAdmin ? isAdmin : true;
@ -141,6 +149,13 @@ export default function SettingsSidebar() {
return true; return true;
}; };
const isItemDisabled = (item: DataItem) => {
if (item.showDisabledInNonEE && item.isEnterprise) {
return !(isCloud() || workspace?.hasLicenseKey);
}
return false;
};
const menuItems = groupedData.map((group) => { const menuItems = groupedData.map((group) => {
if (group.heading === "System" && (!isAdmin || isCloud())) { if (group.heading === "System" && (!isAdmin || isCloud())) {
return null; return null;
@ -185,23 +200,48 @@ export default function SettingsSidebar() {
break; break;
} }
return ( const isDisabled = isItemDisabled(item);
const linkElement = (
<Link <Link
onMouseEnter={prefetchHandler} onMouseEnter={!isDisabled ? prefetchHandler : undefined}
className={classes.link} className={classes.link}
data-active={active.startsWith(item.path) || undefined} data-active={active.startsWith(item.path) || undefined}
data-disabled={isDisabled || undefined}
key={item.label} key={item.label}
to={item.path} to={isDisabled ? "#" : item.path}
onClick={() => { onClick={(e) => {
if (isDisabled) {
e.preventDefault();
return;
}
if (mobileSidebarOpened) { if (mobileSidebarOpened) {
toggleMobileSidebar(); toggleMobileSidebar();
} }
}} }}
style={{
opacity: isDisabled ? 0.5 : 1,
cursor: isDisabled ? "not-allowed" : "pointer",
}}
> >
<item.icon className={classes.linkIcon} stroke={2} /> <item.icon className={classes.linkIcon} stroke={2} />
<span>{t(item.label)}</span> <span>{t(item.label)}</span>
</Link> </Link>
); );
if (isDisabled) {
return (
<Tooltip
key={item.label}
label={t("Available in enterprise edition")}
position="right"
withArrow
>
{linkElement}
</Tooltip>
);
}
return linkElement;
})} })}
</div> </div>
); );

View File

@ -11,7 +11,7 @@ export default function OssDetails() {
withTableBorder withTableBorder
> >
<Table.Caption> <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.Caption>
<Table.Tbody> <Table.Tbody>
<Table.Tr> <Table.Tr>

View File

@ -1,5 +1,5 @@
import React, { useState } from "react"; 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 { useQuery, useQueryClient } from "@tanstack/react-query";
import { notifications } from "@mantine/notifications"; import { notifications } from "@mantine/notifications";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@ -7,6 +7,8 @@ import { getMfaStatus } from "@/ee/mfa";
import { MfaSetupModal } from "@/ee/mfa"; import { MfaSetupModal } from "@/ee/mfa";
import { MfaDisableModal } from "@/ee/mfa"; import { MfaDisableModal } from "@/ee/mfa";
import { MfaBackupCodesModal } 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() { export function MfaSettings() {
const { t } = useTranslation(); const { t } = useTranslation();
@ -14,16 +16,19 @@ export function MfaSettings() {
const [setupModalOpen, setSetupModalOpen] = useState(false); const [setupModalOpen, setSetupModalOpen] = useState(false);
const [disableModalOpen, setDisableModalOpen] = useState(false); const [disableModalOpen, setDisableModalOpen] = useState(false);
const [backupCodesModalOpen, setBackupCodesModalOpen] = useState(false); const [backupCodesModalOpen, setBackupCodesModalOpen] = useState(false);
const { hasLicenseKey } = useLicense();
const { data: mfaStatus, isLoading } = useQuery({ const { data: mfaStatus, isLoading } = useQuery({
queryKey: ["mfa-status"], queryKey: ["mfa-status"],
queryFn: getMfaStatus, queryFn: getMfaStatus,
}); });
if (isLoading) { if (isLoading || !mfaStatus) {
return null; return null;
} }
const canUseMfa = isCloud() || hasLicenseKey;
// Check if MFA is truly enabled // Check if MFA is truly enabled
const isMfaEnabled = mfaStatus?.isEnabled === true; const isMfaEnabled = mfaStatus?.isEnabled === true;
@ -61,13 +66,19 @@ export function MfaSettings() {
</div> </div>
{!isMfaEnabled ? ( {!isMfaEnabled ? (
<Button <Tooltip
variant="default" label={t("Available in enterprise edition")}
onClick={() => setSetupModalOpen(true)} disabled={canUseMfa}
style={{ whiteSpace: "nowrap" }}
> >
{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"> <Group gap="sm" wrap="nowrap">
<Button <Button

View File

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

View File

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