АРХ - МЕЖПРОЕКТНАЯ КОММУНИКАЦИЯ: managedBy split Operational Core
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
import { useState } from "react";
|
||||
import useSWR from "swr";
|
||||
// plane imports
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
@@ -24,11 +25,15 @@ import { useUserPermissions } from "@/hooks/store/user";
|
||||
// plane web components
|
||||
import { BillingActionsButton } from "@/plane-web/components/workspace/billing/billing-actions-button";
|
||||
import { MembersActivityButton, SendWorkspaceInvitationModal } from "@/plane-web/components/workspace/members";
|
||||
// services
|
||||
import { WorkspaceService } from "@/services/workspace.service";
|
||||
|
||||
type TWorkspaceMembersSettingsContentProps = {
|
||||
workspaceSlug: string;
|
||||
};
|
||||
|
||||
const workspaceService = new WorkspaceService();
|
||||
|
||||
export function WorkspaceMembersSettingsContent({ workspaceSlug }: TWorkspaceMembersSettingsContentProps) {
|
||||
const [inviteModal, setInviteModal] = useState(false);
|
||||
const [searchQuery, setSearchQuery] = useState<string>("");
|
||||
@@ -37,6 +42,9 @@ export function WorkspaceMembersSettingsContent({ workspaceSlug }: TWorkspaceMem
|
||||
workspace: { workspaceMemberIds, inviteMembersToWorkspace, filtersStore },
|
||||
} = useMember();
|
||||
const { t } = useTranslation();
|
||||
const { data: nodedcWorkspacePolicy } = useSWR(`NODEDC_WORKSPACE_POLICY_${workspaceSlug}`, () =>
|
||||
workspaceService.getNodeDCWorkspacePolicy(workspaceSlug)
|
||||
);
|
||||
|
||||
const canPerformWorkspaceAdminActions = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
|
||||
const canPerformWorkspaceMemberActions = allowPermissions(
|
||||
@@ -79,6 +87,21 @@ export function WorkspaceMembersSettingsContent({ workspaceSlug }: TWorkspaceMem
|
||||
return <NotAuthorizedView section="settings" className="h-auto" />;
|
||||
}
|
||||
|
||||
if (nodedcWorkspacePolicy?.managed_by === "launcher") {
|
||||
return (
|
||||
<section className="flex size-full flex-col items-start justify-center gap-4 rounded-2xl border border-custom-border-200 bg-custom-background-90 p-8 text-left">
|
||||
<p className="text-sm font-semibold uppercase tracking-[0.22em] text-custom-text-300">NODE.DC managed workspace</p>
|
||||
<div className="max-w-2xl space-y-3">
|
||||
<h4 className="text-h3-medium">Участники управляются в Launcher.</h4>
|
||||
<p className="text-body-sm-regular text-custom-text-300">
|
||||
Этот workspace подключен к enterprise-контуру NODE.DC. Добавление пользователей, инвайты, роли workspace и
|
||||
проектные назначения выполняются через Launcher, чтобы Task Manager не стал вторым источником прав.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<SendWorkspaceInvitationModal
|
||||
|
||||
@@ -35,6 +35,7 @@ import { useUserPermissions } from "@/hooks/store/user";
|
||||
import { useWorkspace } from "@/hooks/store/use-workspace";
|
||||
// services
|
||||
import { WorkspaceAIService } from "@/services/workspace-ai.service";
|
||||
import { WorkspaceService } from "@/services/workspace.service";
|
||||
// local imports
|
||||
import {
|
||||
closeWorkspaceSettingsModal,
|
||||
@@ -46,9 +47,11 @@ import {
|
||||
} from "./workspace-settings-modal.utils";
|
||||
|
||||
const HIDDEN_WORKSPACE_SETTINGS_KEYS = new Set<TWorkspaceSettingsTabs>(["billing-and-plans"]);
|
||||
const LAUNCHER_MANAGED_HIDDEN_WORKSPACE_SETTINGS_KEYS = new Set<TWorkspaceSettingsTabs>(["members"]);
|
||||
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 workspaceService = new WorkspaceService();
|
||||
|
||||
const getInitialTab = (): TWorkspaceSettingsModalTab => {
|
||||
if (typeof window === "undefined") return "general";
|
||||
@@ -79,7 +82,12 @@ export const WorkspaceSettingsModal = observer(function WorkspaceSettingsModal()
|
||||
canLoadVoiceTaskerEntitlement ? `WORKSPACE_AI_SETTINGS_${currentWorkspace.slug}` : null,
|
||||
() => workspaceAIService.retrieveSettings(currentWorkspace?.slug as string)
|
||||
);
|
||||
const { data: nodedcWorkspacePolicy } = useSWR(
|
||||
currentWorkspace?.slug ? `NODEDC_WORKSPACE_POLICY_${currentWorkspace.slug}` : null,
|
||||
() => workspaceService.getNodeDCWorkspacePolicy(currentWorkspace?.slug as string)
|
||||
);
|
||||
const isVoiceTaskerEntitled = aiSettings?.feature_entitlement_enabled === true;
|
||||
const isLauncherManagedWorkspace = nodedcWorkspacePolicy?.managed_by === "launcher";
|
||||
|
||||
useEffect(() => {
|
||||
const syncFromLocation = () => {
|
||||
@@ -115,6 +123,11 @@ export const WorkspaceSettingsModal = observer(function WorkspaceSettingsModal()
|
||||
if (!isVoiceTaskerEntitled) openWorkspaceSettingsModal("general", true);
|
||||
}, [activeTab, isOpen, isVoiceTaskerEntitlementLoading, isVoiceTaskerEntitled]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen || activeTab !== "members" || !isLauncherManagedWorkspace) return;
|
||||
openWorkspaceSettingsModal("general", true);
|
||||
}, [activeTab, isLauncherManagedWorkspace, isOpen]);
|
||||
|
||||
const handleClose = () => {
|
||||
closeWorkspaceSettingsModal();
|
||||
};
|
||||
@@ -175,6 +188,7 @@ export const WorkspaceSettingsModal = observer(function WorkspaceSettingsModal()
|
||||
onSelectItem={handleSelectItem}
|
||||
allowPermissions={allowPermissions}
|
||||
isVoiceTaskerEntitled={isVoiceTaskerEntitled}
|
||||
isLauncherManagedWorkspace={isLauncherManagedWorkspace}
|
||||
workspaceSlug={currentWorkspace?.slug}
|
||||
/>
|
||||
</div>
|
||||
@@ -209,6 +223,7 @@ export const WorkspaceSettingsModal = observer(function WorkspaceSettingsModal()
|
||||
type TWorkspaceModalSidebarProps = {
|
||||
activeTab: TWorkspaceSettingsModalTab;
|
||||
allowPermissions: ReturnType<typeof useUserPermissions>["allowPermissions"];
|
||||
isLauncherManagedWorkspace: boolean;
|
||||
isVoiceTaskerEntitled: boolean;
|
||||
onSelectItem: (itemKey: TWorkspaceSettingsTabs, itemHref: string) => void;
|
||||
workspaceSlug?: string;
|
||||
@@ -217,6 +232,7 @@ type TWorkspaceModalSidebarProps = {
|
||||
function WorkspaceModalSidebar({
|
||||
activeTab,
|
||||
allowPermissions,
|
||||
isLauncherManagedWorkspace,
|
||||
isVoiceTaskerEntitled,
|
||||
onSelectItem,
|
||||
workspaceSlug,
|
||||
@@ -235,6 +251,7 @@ function WorkspaceModalSidebar({
|
||||
const accessibleItems = GROUPED_WORKSPACE_SETTINGS[category].filter(
|
||||
(item) =>
|
||||
!HIDDEN_WORKSPACE_SETTINGS_KEYS.has(item.key) &&
|
||||
(!isLauncherManagedWorkspace || !LAUNCHER_MANAGED_HIDDEN_WORKSPACE_SETTINGS_KEYS.has(item.key)) &&
|
||||
(!WORKSPACE_FEATURE_GATED_SETTINGS_KEYS.has(item.key) || isVoiceTaskerEntitled) &&
|
||||
allowPermissions(item.access, EUserPermissionsLevel.WORKSPACE, workspaceSlug)
|
||||
);
|
||||
|
||||
@@ -39,6 +39,16 @@ export interface NodeDCWorkspacePolicy {
|
||||
enabled: boolean;
|
||||
can_create_workspace: boolean;
|
||||
mode: string;
|
||||
managed_by: "launcher" | "tasker";
|
||||
default_managed_by: "launcher" | "tasker";
|
||||
workspaces: Array<{
|
||||
slug: string;
|
||||
name: string | null;
|
||||
managed_by: "launcher" | "tasker";
|
||||
client_id: string | null;
|
||||
client_name: string | null;
|
||||
role: "guest" | "member" | "admin";
|
||||
}>;
|
||||
reason: string;
|
||||
}
|
||||
|
||||
@@ -102,8 +112,9 @@ export class WorkspaceService extends APIService {
|
||||
});
|
||||
}
|
||||
|
||||
async getNodeDCWorkspacePolicy(): Promise<NodeDCWorkspacePolicy> {
|
||||
return this.get("/api/nodedc/workspace-policy/")
|
||||
async getNodeDCWorkspacePolicy(workspaceSlug?: string): Promise<NodeDCWorkspacePolicy> {
|
||||
const params = workspaceSlug ? `?workspace_slug=${encodeURIComponent(workspaceSlug)}` : "";
|
||||
return this.get(`/api/nodedc/workspace-policy/${params}`)
|
||||
.then((response) => response?.data)
|
||||
.catch((error) => {
|
||||
throw error?.response?.data;
|
||||
|
||||
Reference in New Issue
Block a user