ФУНКЦИИ - МЕЖПРОЕКТНАЯ КОММУНИКАЦИЯ: администрирование воркспейсов и feature-gates в God Mode
This commit is contained in:
@@ -7,18 +7,24 @@
|
||||
import { observer } from "mobx-react";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { useParams } from "react-router";
|
||||
import useSWR from "swr";
|
||||
// plane imports
|
||||
import { EUserPermissionsLevel, GROUPED_WORKSPACE_SETTINGS, WORKSPACE_SETTINGS_CATEGORIES } from "@plane/constants";
|
||||
import { EUserPermissionsLevel, GROUPED_WORKSPACE_SETTINGS, WORKSPACE_SETTINGS, WORKSPACE_SETTINGS_CATEGORIES } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type { TWorkspaceSettingsTabs } from "@plane/types";
|
||||
import { joinUrlPath } from "@plane/utils";
|
||||
// components
|
||||
import { SettingsSidebarItem } from "@/components/settings/sidebar/item";
|
||||
// hooks
|
||||
import { useUserPermissions } from "@/hooks/store/user";
|
||||
// services
|
||||
import { WorkspaceAIService } from "@/services/workspace-ai.service";
|
||||
// local imports
|
||||
import { WORKSPACE_SETTINGS_ICONS } from "./item-icon";
|
||||
|
||||
const HIDDEN_WORKSPACE_SETTINGS_KEYS = new Set(["billing-and-plans"]);
|
||||
const WORKSPACE_FEATURE_GATED_SETTINGS_KEYS = new Set<TWorkspaceSettingsTabs>(["ai-voice-tasker"]);
|
||||
const workspaceAIService = new WorkspaceAIService();
|
||||
|
||||
export const WorkspaceSettingsSidebarItemCategories = observer(function WorkspaceSettingsSidebarItemCategories() {
|
||||
// params
|
||||
@@ -28,6 +34,15 @@ export const WorkspaceSettingsSidebarItemCategories = observer(function Workspac
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
// translation
|
||||
const { t } = useTranslation();
|
||||
// feature gates
|
||||
const canLoadVoiceTaskerEntitlement =
|
||||
!!workspaceSlug &&
|
||||
allowPermissions(WORKSPACE_SETTINGS["ai-voice-tasker"].access, EUserPermissionsLevel.WORKSPACE, workspaceSlug);
|
||||
const { data: aiSettings } = useSWR(
|
||||
canLoadVoiceTaskerEntitlement ? `WORKSPACE_AI_SETTINGS_${workspaceSlug}` : null,
|
||||
() => workspaceAIService.retrieveSettings(workspaceSlug as string)
|
||||
);
|
||||
const isVoiceTaskerEntitled = aiSettings?.feature_entitlement_enabled === true;
|
||||
|
||||
return (
|
||||
<div className="mt-4 flex flex-col divide-y divide-white/6">
|
||||
@@ -36,6 +51,7 @@ export const WorkspaceSettingsSidebarItemCategories = observer(function Workspac
|
||||
const accessibleItems = categoryItems.filter(
|
||||
(item) =>
|
||||
!HIDDEN_WORKSPACE_SETTINGS_KEYS.has(item.key) &&
|
||||
(!WORKSPACE_FEATURE_GATED_SETTINGS_KEYS.has(item.key) || isVoiceTaskerEntitled) &&
|
||||
allowPermissions(item.access, EUserPermissionsLevel.WORKSPACE, workspaceSlug)
|
||||
);
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { X } from "lucide-react";
|
||||
import useSWR from "swr";
|
||||
// plane imports
|
||||
import {
|
||||
EUserPermissionsLevel,
|
||||
@@ -32,6 +33,8 @@ import { WorkspaceDetails } from "@/components/workspace/settings/workspace-deta
|
||||
// hooks
|
||||
import { useUserPermissions } from "@/hooks/store/user";
|
||||
import { useWorkspace } from "@/hooks/store/use-workspace";
|
||||
// services
|
||||
import { WorkspaceAIService } from "@/services/workspace-ai.service";
|
||||
// local imports
|
||||
import {
|
||||
closeWorkspaceSettingsModal,
|
||||
@@ -43,7 +46,9 @@ import {
|
||||
} from "./workspace-settings-modal.utils";
|
||||
|
||||
const HIDDEN_WORKSPACE_SETTINGS_KEYS = new Set<TWorkspaceSettingsTabs>(["billing-and-plans"]);
|
||||
const WORKSPACE_FEATURE_GATED_SETTINGS_KEYS = new Set<TWorkspaceSettingsTabs>(["ai-voice-tasker"]);
|
||||
const MODAL_TABS = new Set<TWorkspaceSettingsTabs>(["general", "members", "export", "storage", "webhooks", "ai-voice-tasker"]);
|
||||
const workspaceAIService = new WorkspaceAIService();
|
||||
|
||||
const getInitialTab = (): TWorkspaceSettingsModalTab => {
|
||||
if (typeof window === "undefined") return "general";
|
||||
@@ -67,6 +72,14 @@ export const WorkspaceSettingsModal = observer(function WorkspaceSettingsModal()
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
const { t } = useTranslation();
|
||||
const canLoadVoiceTaskerEntitlement =
|
||||
!!currentWorkspace?.slug &&
|
||||
allowPermissions(WORKSPACE_SETTINGS["ai-voice-tasker"].access, EUserPermissionsLevel.WORKSPACE, currentWorkspace.slug);
|
||||
const { data: aiSettings, isLoading: isVoiceTaskerEntitlementLoading } = useSWR(
|
||||
canLoadVoiceTaskerEntitlement ? `WORKSPACE_AI_SETTINGS_${currentWorkspace.slug}` : null,
|
||||
() => workspaceAIService.retrieveSettings(currentWorkspace?.slug as string)
|
||||
);
|
||||
const isVoiceTaskerEntitled = aiSettings?.feature_entitlement_enabled === true;
|
||||
|
||||
useEffect(() => {
|
||||
const syncFromLocation = () => {
|
||||
@@ -97,6 +110,11 @@ export const WorkspaceSettingsModal = observer(function WorkspaceSettingsModal()
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen || activeTab !== "ai-voice-tasker" || isVoiceTaskerEntitlementLoading) return;
|
||||
if (!isVoiceTaskerEntitled) openWorkspaceSettingsModal("general", true);
|
||||
}, [activeTab, isOpen, isVoiceTaskerEntitlementLoading, isVoiceTaskerEntitled]);
|
||||
|
||||
const handleClose = () => {
|
||||
closeWorkspaceSettingsModal();
|
||||
};
|
||||
@@ -114,6 +132,7 @@ export const WorkspaceSettingsModal = observer(function WorkspaceSettingsModal()
|
||||
|
||||
const renderContent = () => {
|
||||
if (activeTab === "ai-voice-tasker" && currentWorkspace?.slug) {
|
||||
if (!isVoiceTaskerEntitled) return <WorkspaceDetails />;
|
||||
return <AIVoiceTaskerSettingsContent workspaceSlug={currentWorkspace.slug} />;
|
||||
}
|
||||
|
||||
@@ -155,6 +174,7 @@ export const WorkspaceSettingsModal = observer(function WorkspaceSettingsModal()
|
||||
activeTab={activeTab}
|
||||
onSelectItem={handleSelectItem}
|
||||
allowPermissions={allowPermissions}
|
||||
isVoiceTaskerEntitled={isVoiceTaskerEntitled}
|
||||
workspaceSlug={currentWorkspace?.slug}
|
||||
/>
|
||||
</div>
|
||||
@@ -189,11 +209,18 @@ export const WorkspaceSettingsModal = observer(function WorkspaceSettingsModal()
|
||||
type TWorkspaceModalSidebarProps = {
|
||||
activeTab: TWorkspaceSettingsModalTab;
|
||||
allowPermissions: ReturnType<typeof useUserPermissions>["allowPermissions"];
|
||||
isVoiceTaskerEntitled: boolean;
|
||||
onSelectItem: (itemKey: TWorkspaceSettingsTabs, itemHref: string) => void;
|
||||
workspaceSlug?: string;
|
||||
};
|
||||
|
||||
function WorkspaceModalSidebar({ activeTab, allowPermissions, onSelectItem, workspaceSlug }: TWorkspaceModalSidebarProps) {
|
||||
function WorkspaceModalSidebar({
|
||||
activeTab,
|
||||
allowPermissions,
|
||||
isVoiceTaskerEntitled,
|
||||
onSelectItem,
|
||||
workspaceSlug,
|
||||
}: TWorkspaceModalSidebarProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
@@ -208,6 +235,7 @@ function WorkspaceModalSidebar({ activeTab, allowPermissions, onSelectItem, work
|
||||
const accessibleItems = GROUPED_WORKSPACE_SETTINGS[category].filter(
|
||||
(item) =>
|
||||
!HIDDEN_WORKSPACE_SETTINGS_KEYS.has(item.key) &&
|
||||
(!WORKSPACE_FEATURE_GATED_SETTINGS_KEYS.has(item.key) || isVoiceTaskerEntitled) &&
|
||||
allowPermissions(item.access, EUserPermissionsLevel.WORKSPACE, workspaceSlug)
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user