FIX - МЕЖПРОЕКТНАЯ КОММУНИКАЦИЯ: АДМИНКА РОЛЕЙ TASKER
This commit is contained in:
@@ -64,7 +64,7 @@ import {
|
||||
type MeResponse,
|
||||
type TaskManagerWorkspaceCreationPolicy,
|
||||
} from "../../shared/api/mockApi";
|
||||
import type { TaskManagerWorkspaceSummary } from "../../shared/api/adminApi";
|
||||
import type { TaskManagerWorkspaceMemberRole, TaskManagerWorkspaceSummary } from "../../shared/api/adminApi";
|
||||
import { uploadStorageFile } from "../../shared/api/storageApi";
|
||||
import { cn } from "../../shared/lib/cn";
|
||||
import { formatDate, formatDateTime } from "../../shared/lib/format";
|
||||
@@ -87,6 +87,7 @@ type AdminSection =
|
||||
|
||||
type AccessAssignmentRole = Exclude<ServiceAppRole, "owner">;
|
||||
export type AccessAssignmentValue = AccessAssignmentRole | "deny" | "unset";
|
||||
type TaskManagerRoleSelectValue = TaskManagerWorkspaceMemberRole | "pending";
|
||||
|
||||
export interface SetUserServiceAccessCommand {
|
||||
userId: string;
|
||||
@@ -107,7 +108,7 @@ export interface CreateUserCommand {
|
||||
export interface EnsureTaskManagerWorkspaceMemberCommand {
|
||||
clientId: string;
|
||||
userId: string;
|
||||
role?: "member" | "admin";
|
||||
role: TaskManagerWorkspaceMemberRole;
|
||||
}
|
||||
|
||||
const rootSections: Array<{ id: AdminSection; label: string; icon: React.ReactNode }> = [
|
||||
@@ -164,7 +165,7 @@ export function AdminOverlay({
|
||||
taskManagerWorkspacesError,
|
||||
pendingTaskManagerMemberships,
|
||||
onRefreshTaskManagerWorkspaces,
|
||||
onEnsureTaskManagerWorkspaceMember,
|
||||
onSetTaskManagerWorkspaceMemberRole,
|
||||
}: {
|
||||
data: LauncherData;
|
||||
me: MeResponse;
|
||||
@@ -196,7 +197,7 @@ export function AdminOverlay({
|
||||
taskManagerWorkspacesError: string | null;
|
||||
pendingTaskManagerMemberships: Record<string, boolean>;
|
||||
onRefreshTaskManagerWorkspaces: () => void;
|
||||
onEnsureTaskManagerWorkspaceMember: (command: EnsureTaskManagerWorkspaceMemberCommand) => void;
|
||||
onSetTaskManagerWorkspaceMemberRole: (command: EnsureTaskManagerWorkspaceMemberCommand) => void;
|
||||
}) {
|
||||
const isRoot = me.launcherRole === "root_admin";
|
||||
const sections = isRoot ? rootSections : clientSections;
|
||||
@@ -329,7 +330,7 @@ export function AdminOverlay({
|
||||
onUpdateMembership={onUpdateMembership}
|
||||
onDeleteMembership={onDeleteMembership}
|
||||
pendingTaskManagerMemberships={pendingTaskManagerMemberships}
|
||||
onEnsureTaskManagerWorkspaceMember={onEnsureTaskManagerWorkspaceMember}
|
||||
onSetTaskManagerWorkspaceMemberRole={onSetTaskManagerWorkspaceMemberRole}
|
||||
/>
|
||||
) : null}
|
||||
{activeSection === "groups" ? (
|
||||
@@ -570,7 +571,7 @@ function UsersSection({
|
||||
onUpdateMembership,
|
||||
onDeleteMembership,
|
||||
pendingTaskManagerMemberships,
|
||||
onEnsureTaskManagerWorkspaceMember,
|
||||
onSetTaskManagerWorkspaceMemberRole,
|
||||
}: {
|
||||
data: LauncherData;
|
||||
clientId: string;
|
||||
@@ -580,7 +581,7 @@ function UsersSection({
|
||||
onUpdateMembership: (membershipId: string, patch: Partial<ClientMembership>) => void;
|
||||
onDeleteMembership: (membershipId: string) => void;
|
||||
pendingTaskManagerMemberships: Record<string, boolean>;
|
||||
onEnsureTaskManagerWorkspaceMember: (command: EnsureTaskManagerWorkspaceMemberCommand) => void;
|
||||
onSetTaskManagerWorkspaceMemberRole: (command: EnsureTaskManagerWorkspaceMemberCommand) => void;
|
||||
}) {
|
||||
const [editingMembershipId, setEditingMembershipId] = useState<string | null>(null);
|
||||
const [newUserEmail, setNewUserEmail] = useState("");
|
||||
@@ -661,11 +662,11 @@ function UsersSection({
|
||||
</div>
|
||||
</GlassSurface>
|
||||
|
||||
<GlassSurface className="table-shell">
|
||||
<GlassSurface className="table-shell table-shell--users">
|
||||
<div className="table-toolbar">
|
||||
<h3>Участники</h3>
|
||||
</div>
|
||||
<table className="admin-data-table">
|
||||
<table className="admin-data-table admin-data-table--users">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Пользователь</th>
|
||||
@@ -683,7 +684,15 @@ function UsersSection({
|
||||
const taskManagerWorkspace = client.integrations?.taskManager?.workspaceSlug ?? null;
|
||||
const pendingKey = `${client.id}:${user.id}`;
|
||||
const pendingTaskerAssignment = Boolean(pendingTaskManagerMemberships[pendingKey]);
|
||||
const taskManagerRole = membership.role === "client_owner" || membership.role === "client_admin" ? "admin" : "member";
|
||||
const forcedTaskManagerAdmin = membership.role === "client_owner";
|
||||
const taskManagerAssignment = data.taskManagerMemberships.find(
|
||||
(candidate) => candidate.clientId === client.id && candidate.userId === user.id && candidate.workspaceSlug === taskManagerWorkspace
|
||||
);
|
||||
const taskManagerRole = taskManagerAssignment?.role ?? (forcedTaskManagerAdmin ? "admin" : "unset");
|
||||
const taskManagerRoleOptions = buildTaskManagerRoleOptions({
|
||||
hasWorkspace: Boolean(taskManagerWorkspace),
|
||||
disabled: pendingTaskerAssignment || forcedTaskManagerAdmin || membership.status !== "active" || user.globalStatus !== "active",
|
||||
});
|
||||
|
||||
return (
|
||||
<tr key={membership.id}>
|
||||
@@ -736,15 +745,25 @@ function UsersSection({
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
className="admin-inline-action"
|
||||
type="button"
|
||||
disabled={!taskManagerWorkspace || pendingTaskerAssignment || membership.status !== "active" || user.globalStatus !== "active"}
|
||||
title={taskManagerWorkspace ? `Workspace: ${taskManagerWorkspace}` : "У клиента не выбран workspace Operational Core"}
|
||||
onClick={() => onEnsureTaskManagerWorkspaceMember({ clientId: client.id, userId: user.id, role: taskManagerRole })}
|
||||
>
|
||||
{pendingTaskerAssignment ? "Назначаем" : taskManagerWorkspace ? "Назначить" : "Нет workspace"}
|
||||
</button>
|
||||
<NodeDcSelect
|
||||
className="admin-table-select-wrap"
|
||||
triggerClassName="admin-table-select-trigger"
|
||||
value={pendingTaskerAssignment ? "pending" : taskManagerRole}
|
||||
options={taskManagerRoleOptions}
|
||||
label={`Роль Tasker ${user.name}`}
|
||||
minMenuWidth={180}
|
||||
disabled={
|
||||
!taskManagerWorkspace ||
|
||||
pendingTaskerAssignment ||
|
||||
forcedTaskManagerAdmin ||
|
||||
membership.status !== "active" ||
|
||||
user.globalStatus !== "active"
|
||||
}
|
||||
onChange={(role) => {
|
||||
if (role === "pending") return;
|
||||
onSetTaskManagerWorkspaceMemberRole({ clientId: client.id, userId: user.id, role });
|
||||
}}
|
||||
/>
|
||||
</td>
|
||||
<td className="services-admin-table__actions">
|
||||
<IconButton
|
||||
@@ -951,6 +970,22 @@ const accessAssignmentOptions: Array<NodeDcSelectOption<AccessAssignmentValue>>
|
||||
{ value: "deny", label: "Deny", description: "Исключение", tone: "red" },
|
||||
];
|
||||
|
||||
function buildTaskManagerRoleOptions({
|
||||
hasWorkspace,
|
||||
disabled,
|
||||
}: {
|
||||
hasWorkspace: boolean;
|
||||
disabled: boolean;
|
||||
}): Array<NodeDcSelectOption<TaskManagerRoleSelectValue>> {
|
||||
return [
|
||||
{ value: "unset", label: hasWorkspace ? "—" : "Workspace не выбран" },
|
||||
{ value: "guest", label: "Гость", disabled: !hasWorkspace || disabled },
|
||||
{ value: "member", label: "Участник", disabled: !hasWorkspace || disabled },
|
||||
{ value: "admin", label: "Админ", disabled: !hasWorkspace || disabled },
|
||||
{ value: "pending", label: "Сохраняем...", disabled: true, hidden: true },
|
||||
];
|
||||
}
|
||||
|
||||
const taskManagerWorkspacePolicyOptions: Array<NodeDcSelectOption<TaskManagerWorkspaceCreationPolicy>> = [
|
||||
{
|
||||
value: "any_authorized_user",
|
||||
@@ -1651,9 +1686,15 @@ function ClientEditorModal({
|
||||
minMenuWidth={280}
|
||||
onChange={updateTaskManagerWorkspace}
|
||||
/>
|
||||
<button className="admin-inline-action" type="button" disabled={taskManagerWorkspacesLoading} onClick={onRefreshTaskManagerWorkspaces}>
|
||||
{taskManagerWorkspacesLoading ? "Обновляем" : "Обновить"}
|
||||
</button>
|
||||
<IconButton
|
||||
label={taskManagerWorkspacesLoading ? "Обновляем workspace Operational Core" : "Обновить workspace Operational Core"}
|
||||
className="admin-circle-action admin-circle-action--solid"
|
||||
type="button"
|
||||
disabled={taskManagerWorkspacesLoading}
|
||||
onClick={onRefreshTaskManagerWorkspaces}
|
||||
>
|
||||
<RefreshCw size={16} />
|
||||
</IconButton>
|
||||
</div>
|
||||
<small>
|
||||
{taskManagerWorkspacesError
|
||||
|
||||
Reference in New Issue
Block a user