From 483d6b1a0c3a19ea1b140f6b2b77b5231bbf81d8 Mon Sep 17 00:00:00 2001 From: YankinA Date: Wed, 15 Oct 2025 12:50:25 +0300 Subject: [PATCH 01/25] add types --- .../extensions/actions/iam-access-dialog.ts | 11 +++++++ .../extensions/types/iam-access-dialog.ts | 30 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/src/shared/schema/extensions/actions/iam-access-dialog.ts b/src/shared/schema/extensions/actions/iam-access-dialog.ts index 71c79acfe4..1f66936bed 100644 --- a/src/shared/schema/extensions/actions/iam-access-dialog.ts +++ b/src/shared/schema/extensions/actions/iam-access-dialog.ts @@ -1,6 +1,8 @@ import {createAction} from '../../gateway-utils'; import type {GetDatalensOperationResponse} from '../../us/types/operations'; import type { + BatchListAccessBindingsArgs, + BatchListAccessBindingsResponse, BatchListMembersArgs, BatchListMembersResponse, GetClaimsArgs, @@ -40,4 +42,13 @@ export const iamAccessDialogActions = { batchListMembers: createAction(async () => { return {members: [], nextPageToken: ''}; }), + batchListAccessBindings: createAction< + BatchListAccessBindingsResponse, + BatchListAccessBindingsArgs + >(async () => { + return { + subjectsWithBindings: [], + nextPageToken: '', + }; + }), }; diff --git a/src/shared/schema/extensions/types/iam-access-dialog.ts b/src/shared/schema/extensions/types/iam-access-dialog.ts index 743e570459..1ff28c57a1 100644 --- a/src/shared/schema/extensions/types/iam-access-dialog.ts +++ b/src/shared/schema/extensions/types/iam-access-dialog.ts @@ -20,6 +20,7 @@ export enum SubjectType { } export enum ClaimsSubjectType { + Unspecified = 'SUBJECT_TYPE_UNSPECIFIED', UserAccount = 'USER_ACCOUNT', Group = 'GROUP', Invitee = 'INVITEE', @@ -131,3 +132,32 @@ export type BatchListMembersResponse = { members: SubjectClaims[]; nextPageToken: string; }; + +export interface BatchListAccessBindingsResponse { + subjectsWithBindings: SubjectWithBindings[]; + nextPageToken: string; +} + +export interface SubjectWithBindings { + subjectClaims: SubjectClaims; + accessBindings: InheritedAccessBindings[]; + inheritedAccessBindings: InheritedAccessBindings[]; +} + +export interface InheritedAccessBindings { + roleId: string; + inheritedFrom: AccessBindingsResource | null; +} + +export interface AccessBindingsResource { + id: string; + type: string; +} + +export type BatchListAccessBindingsArgs = { + resourcePath: AccessBindingsResource[]; + getInheritedBindings?: boolean; + filter?: string; + pageSize?: number; + pageToken?: string; +}; From b9d08a5837776034b2cd2e1087bb9bc5ee3891c9 Mon Sep 17 00:00:00 2001 From: YankinA Date: Tue, 28 Oct 2025 17:16:20 +0300 Subject: [PATCH 02/25] AccessDialog --- .../component.iam-access-dialog/en.json | 8 ++++++- .../component.iam-access-dialog/ru.json | 8 ++++++- .../extensions/types/iam-access-dialog.ts | 5 +++++ .../registry/units/common/components-map.tsx | 1 + src/ui/registry/units/common/functions-map.ts | 2 ++ .../types/functions/onOpenAcceessDialog.ts | 1 + .../WorkbookActions/WorkbookActions.tsx | 21 ++++++++++++++++++- 7 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 src/ui/registry/units/common/types/functions/onOpenAcceessDialog.ts diff --git a/src/i18n-keysets/component.iam-access-dialog/en.json b/src/i18n-keysets/component.iam-access-dialog/en.json index 18fdc67f36..229c6e95a0 100644 --- a/src/i18n-keysets/component.iam-access-dialog/en.json +++ b/src/i18n-keysets/component.iam-access-dialog/en.json @@ -7,6 +7,7 @@ "action_revoke-access": "Revoke role", "action_save": "Save", "button_bindings-list-retry": "Reload list", + "label_access": "Access rights", "label_access_error": "Insufficient rights", "label_all": "All", "label_groups": "Groups", @@ -14,6 +15,8 @@ "label_no-data": "Empty", "label_object": "Object", "label_revoke-access": "Revoke role", + "label_delete": "Delete", + "label_to": "To", "label_role": "Role", "label_user": "User", "label_user-accounts": "Users", @@ -21,10 +24,13 @@ "role_admin": "Admin", "role_editor": "Editor", "role_limited-viewer": "Limited viewer", + "role_limited-viewer-hint": "Unlike 'Viewer', it doesn't grant access to view datasets and connections", "role_viewer": "Viewer", "section_direct_accesses": "Direct permissions", "section_inherited_accesses": "Inherited permissions", "section_revoke-access": "Are you sure you want to revoke this role from {{userName}}? The user will lose access to all objects located in {{objectName}}.", "title_access-list": "Manage access", - "title_add-user": "Add user" + "title_add-user": "Add user", + "placeholder_select-option": "Not selected", + "placeholder_select_subject_type": "Groups and users" } diff --git a/src/i18n-keysets/component.iam-access-dialog/ru.json b/src/i18n-keysets/component.iam-access-dialog/ru.json index 51f53c243a..bbf2798bbd 100644 --- a/src/i18n-keysets/component.iam-access-dialog/ru.json +++ b/src/i18n-keysets/component.iam-access-dialog/ru.json @@ -7,6 +7,7 @@ "action_revoke-access": "Отозвать роль", "action_save": "Сохранить", "button_bindings-list-retry": "Обновить список", + "label_access": "Права доступа", "label_access_error": "Недостаточно прав", "label_all": "Все", "label_groups": "Группы", @@ -14,6 +15,8 @@ "label_no-data": "Пусто", "label_object": "Объект", "label_revoke-access": "Отозвать роль", + "label_delete": "Удалить", + "label_to": "Кому", "label_role": "Роль", "label_user": "Пользователь", "label_user-accounts": "Пользователи", @@ -21,10 +24,13 @@ "role_admin": "Администрирование", "role_editor": "Редактирование", "role_limited-viewer": "Ограниченный просмотр", + "role_limited-viewer-hint": "В отличие от просмотра, не даёт доступ просмотру датасетов и подключений", "role_viewer": "Просмотр", "section_direct_accesses": "Прямые права", "section_inherited_accesses": "Наследуемые права", "section_revoke-access": "Вы уверены, что хотите отозвать роль у {{userName}}? Пользователь потеряет доступ ко всем объектам находящимся в {{objectName}}.", "title_access-list": "Управление доступом", - "title_add-user": "Добавление пользователя" + "title_add-user": "Добавление пользователя", + "placeholder_select-option": "Не выбранно", + "placeholder_select_subject_type": "Группы и пользователи" } diff --git a/src/shared/schema/extensions/types/iam-access-dialog.ts b/src/shared/schema/extensions/types/iam-access-dialog.ts index 1ff28c57a1..91926d0da4 100644 --- a/src/shared/schema/extensions/types/iam-access-dialog.ts +++ b/src/shared/schema/extensions/types/iam-access-dialog.ts @@ -161,3 +161,8 @@ export type BatchListAccessBindingsArgs = { pageSize?: number; pageToken?: string; }; + +export enum ResourceType { + Collection = 'collection', + Workbook = 'workbook', +} diff --git a/src/ui/registry/units/common/components-map.tsx b/src/ui/registry/units/common/components-map.tsx index fc1ce12f31..0e423ed9a1 100644 --- a/src/ui/registry/units/common/components-map.tsx +++ b/src/ui/registry/units/common/components-map.tsx @@ -63,4 +63,5 @@ export const commonComponentsMap = { DialogRelatedEntitiesRadioHint: makeDefaultEmpty(), WorkbookEntriesTableTabs: makeDefaultEmpty(), DialogEntryDescription: makeDefaultEmpty(), + AccessDialog: makeDefaultEmpty(), } as const; diff --git a/src/ui/registry/units/common/functions-map.ts b/src/ui/registry/units/common/functions-map.ts index ad27a72df8..52111625b3 100644 --- a/src/ui/registry/units/common/functions-map.ts +++ b/src/ui/registry/units/common/functions-map.ts @@ -50,6 +50,7 @@ import type {GetLogoIcon} from './types/functions/getLogoIcon'; import type {GetShouldShowAIAssistant} from './types/functions/getShouldShowAIAssistant'; import type {GetUIEntryRouteArgs} from './types/functions/getUIEntryRoute'; import type {IsValidLogoUrl} from './types/functions/isValidLogoUrl'; +import type {OpenAccessDialog} from './types/functions/onOpenAcceessDialog'; import type {OpenDialogOrganizationInvite} from './types/functions/openDialogOrganizationInvite'; import type {OpenDialogOrganizationInviteUsers} from './types/functions/openDialogOrganizationInviteUsers'; import type {RenderDialogRelatedEntitiesAlertHint} from './types/functions/renderDialogRelatedEntitiesAlertHint'; @@ -178,4 +179,5 @@ export const commonFunctionsMap = { getLogoIcon: makeFunctionTemplate(), isValidLogoUrl: makeFunctionTemplate(), getShouldShowAIAssistant: makeFunctionTemplate(), + openAccessDialog: makeFunctionTemplate(), } as const; diff --git a/src/ui/registry/units/common/types/functions/onOpenAcceessDialog.ts b/src/ui/registry/units/common/types/functions/onOpenAcceessDialog.ts new file mode 100644 index 0000000000..a7811c429c --- /dev/null +++ b/src/ui/registry/units/common/types/functions/onOpenAcceessDialog.ts @@ -0,0 +1 @@ +export type OpenAccessDialog = (arg: any) => void; diff --git a/src/ui/units/workbooks/components/WorkbookActions/WorkbookActions.tsx b/src/ui/units/workbooks/components/WorkbookActions/WorkbookActions.tsx index 1aecba554d..3f3b848e9d 100644 --- a/src/ui/units/workbooks/components/WorkbookActions/WorkbookActions.tsx +++ b/src/ui/units/workbooks/components/WorkbookActions/WorkbookActions.tsx @@ -193,6 +193,23 @@ export const WorkbookActions: React.FC = ({workbook, refreshWorkbookInfo} dropdownActions.push([...otherActions]); } + const {openAccessDialog} = registry.common.functions.getAll(); + + const onOpenAccessDialog = React.useCallback(() => { + //Access list TODO this is draft only + dispatch( + openAccessDialog({ + resourceId: workbook.workbookId, + resourceType: ResourceType.Workbook, + resourceTitle: workbook.title, + canUpdate: workbook.permissions.updateAccessBindings, + }), + ); + // setIamAccessDialogIsOpen(true); + }, []); + + const {AccessDialog} = registry.common.components.getAll(); + return (
{Boolean(dropdownActions.length) && ( @@ -210,7 +227,7 @@ export const WorkbookActions: React.FC = ({workbook, refreshWorkbookInfo}
)} + + {collectionsAccessEnabled && workbook.permissions.listAccessBindings && ( Date: Thu, 30 Oct 2025 12:03:26 +0300 Subject: [PATCH 03/25] add MultiSelectInputInput --- .../MultiSelectInput/MultiSelectInput.scss | 31 +++++++ src/ui/components/MultiSelectInput/index.tsx | 89 +++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 src/ui/components/MultiSelectInput/MultiSelectInput.scss create mode 100644 src/ui/components/MultiSelectInput/index.tsx diff --git a/src/ui/components/MultiSelectInput/MultiSelectInput.scss b/src/ui/components/MultiSelectInput/MultiSelectInput.scss new file mode 100644 index 0000000000..762ffb1e7d --- /dev/null +++ b/src/ui/components/MultiSelectInput/MultiSelectInput.scss @@ -0,0 +1,31 @@ +$block: '.dl-multi-select-input'; + +#{$block} { + width: var(--multi-select-width, 'auto'); + display: flex; + flex-direction: row; + justify-content: flex-start; + align-items: start; + padding: 5px; + + &__item-list { + width: 81%; + display: flex; + flex-wrap: wrap; + gap: 7px; + justify-content: flex-start; + align-items: center; + flex: 1; + } + + &__input-wrapper { + flex: 1; + min-width: 100px; + padding-left: 5px; + } + + &__end-content-wrapper { + flex: 1; + min-width: 100px; + } +} diff --git a/src/ui/components/MultiSelectInput/index.tsx b/src/ui/components/MultiSelectInput/index.tsx new file mode 100644 index 0000000000..85c3b7545f --- /dev/null +++ b/src/ui/components/MultiSelectInput/index.tsx @@ -0,0 +1,89 @@ +import React, {useCallback, useMemo} from 'react'; + +import {Card, Label, TextInput} from '@gravity-ui/uikit'; +import block from 'bem-cn-lite'; + +import './MultiSelectInput.scss'; + +const b = block('dl-multi-select-input'); + +export type MultiSelectInputProps = { + className?: string | undefined; + onUpdate?: (value: string) => void; + value?: string[]; + width?: string | number; + placeholder?: string; + endContent?: React.ReactNode; + onClickValue?: RenderValueArg['onClick']; + onDeleteValue?: RenderValueArg['onDelete']; + renderValue?: (arg: RenderValueArg, i: number) => React.ReactNode; +}; + +type RenderValueArg = { + value: string; + onClick: (value: string) => void; + onDelete?: (value: string) => void; +}; + +export const MultiSelectInput: React.FC = ({ + className, + onUpdate, + value = [], + placeholder, + endContent, + onDeleteValue = (_: string) => {}, + onClickValue = (_: string) => {}, + renderValue, + width, +}) => { + const [valueList, setValueList] = React.useState(value); + + const onDelete = useCallback( + (currentValue: string) => { + onDeleteValue(currentValue); + setValueList((prevValueList) => prevValueList.filter((v) => v !== currentValue)); + }, + [onDeleteValue], + ); + const filteredValueList = useMemo(() => valueList, [valueList]); + + const renderValueComponent = (currentValue: string, i: number) => { + if (renderValue) { + return renderValue({value: currentValue, onDelete, onClick: onClickValue}, i); + } + return ( + + ); + }; + return ( + +
+ {filteredValueList.map(renderValueComponent)} + +
+ +
+
+ {endContent &&
{endContent}
} +
+ ); +}; From 48a0959ac899cca324b13034a6a5a97e3c564fff Mon Sep 17 00:00:00 2001 From: YankinA Date: Wed, 5 Nov 2025 17:44:59 +0300 Subject: [PATCH 04/25] add access dialog func and component --- .../component.iam-access-dialog/en.json | 10 ++++++- .../component.iam-access-dialog/ru.json | 10 ++++++- .../extensions/types/iam-access-dialog.ts | 1 + .../MultiSelectInput/MultiSelectInput.scss | 9 +++--- src/ui/components/MultiSelectInput/index.tsx | 6 ++++ .../registry/units/common/components-map.tsx | 2 ++ .../types/components/AclSubjectSelect.ts | 29 +++++++++++++++++++ .../types/functions/useSubjectsListId.ts | 6 +++- .../WorkbookActions/WorkbookActions.tsx | 9 ++++-- 9 files changed, 71 insertions(+), 11 deletions(-) create mode 100644 src/ui/registry/units/common/types/components/AclSubjectSelect.ts diff --git a/src/i18n-keysets/component.iam-access-dialog/en.json b/src/i18n-keysets/component.iam-access-dialog/en.json index 229c6e95a0..5dee874223 100644 --- a/src/i18n-keysets/component.iam-access-dialog/en.json +++ b/src/i18n-keysets/component.iam-access-dialog/en.json @@ -1,4 +1,5 @@ { + "action_add": "Add", "action_add-user": "Add user", "action_cancel": "Cancel", "action_choose-user": "Select user", @@ -8,6 +9,10 @@ "action_save": "Save", "button_bindings-list-retry": "Reload list", "label_access": "Access rights", + "label_access_name": "Access", + "label_access_workbook": "Workbook", + "label_access_collection": "Collection", + "label_access_organization": "Organization", "label_access_error": "Insufficient rights", "label_all": "All", "label_groups": "Groups", @@ -32,5 +37,8 @@ "title_access-list": "Manage access", "title_add-user": "Add user", "placeholder_select-option": "Not selected", - "placeholder_select_subject_type": "Groups and users" + "placeholder_select_subject_type": "Users and Groups", + "label_search": "Search by name, title or email", + "label_warning-workbook": "You are granting access to the workbook {{resourceTitle}} and all objects inside", + "label_warning-collection": "You are granting access to the collection {{resourceTitle}} and all objects inside" } diff --git a/src/i18n-keysets/component.iam-access-dialog/ru.json b/src/i18n-keysets/component.iam-access-dialog/ru.json index bbf2798bbd..dd7a6181f7 100644 --- a/src/i18n-keysets/component.iam-access-dialog/ru.json +++ b/src/i18n-keysets/component.iam-access-dialog/ru.json @@ -1,4 +1,5 @@ { + "action_add": "Добавить", "action_add-user": "Добавить пользователя", "action_cancel": "Отменить", "action_choose-user": "Выбрать пользователя", @@ -8,6 +9,10 @@ "action_save": "Сохранить", "button_bindings-list-retry": "Обновить список", "label_access": "Права доступа", + "label_access_name": "Права", + "label_access_workbook": "Воркбук", + "label_access_collection": "Коллекция", + "label_access_organization": "Организация", "label_access_error": "Недостаточно прав", "label_all": "Все", "label_groups": "Группы", @@ -32,5 +37,8 @@ "title_access-list": "Управление доступом", "title_add-user": "Добавление пользователя", "placeholder_select-option": "Не выбранно", - "placeholder_select_subject_type": "Группы и пользователи" + "placeholder_select_subject_type": "Пользователи и группы", + "label_search": "Искать по имени, названию или почте", + "label_warning-workbook": "Вы даете доступ к воркбуку {{resourceTitle}} и всем объектам внутри", + "label_warning-collection": "Вы даете доступ к коллекции {{resourceTitle}} и всем объектам внутри" } diff --git a/src/shared/schema/extensions/types/iam-access-dialog.ts b/src/shared/schema/extensions/types/iam-access-dialog.ts index 91926d0da4..965352a1e0 100644 --- a/src/shared/schema/extensions/types/iam-access-dialog.ts +++ b/src/shared/schema/extensions/types/iam-access-dialog.ts @@ -1,6 +1,7 @@ import type {Lang} from '../../..'; export enum AccessServiceResourceType { + Organization = 'organization-manager.organization', Collection = 'datalens.collection', Workbook = 'datalens.workbook', } diff --git a/src/ui/components/MultiSelectInput/MultiSelectInput.scss b/src/ui/components/MultiSelectInput/MultiSelectInput.scss index 762ffb1e7d..34ea114613 100644 --- a/src/ui/components/MultiSelectInput/MultiSelectInput.scss +++ b/src/ui/components/MultiSelectInput/MultiSelectInput.scss @@ -4,15 +4,15 @@ $block: '.dl-multi-select-input'; width: var(--multi-select-width, 'auto'); display: flex; flex-direction: row; - justify-content: flex-start; - align-items: start; - padding: 5px; + justify-content: space-between; + align-items: flex-start; + padding: 5px 7px; &__item-list { width: 81%; display: flex; flex-wrap: wrap; - gap: 7px; + gap: 8px; justify-content: flex-start; align-items: center; flex: 1; @@ -25,7 +25,6 @@ $block: '.dl-multi-select-input'; } &__end-content-wrapper { - flex: 1; min-width: 100px; } } diff --git a/src/ui/components/MultiSelectInput/index.tsx b/src/ui/components/MultiSelectInput/index.tsx index 85c3b7545f..cd83baa8f0 100644 --- a/src/ui/components/MultiSelectInput/index.tsx +++ b/src/ui/components/MultiSelectInput/index.tsx @@ -10,6 +10,8 @@ const b = block('dl-multi-select-input'); export type MultiSelectInputProps = { className?: string | undefined; onUpdate?: (value: string) => void; + onInputBlur?: React.FocusEventHandler | undefined; + onInputFocus?: React.FocusEventHandler | undefined; value?: string[]; width?: string | number; placeholder?: string; @@ -28,6 +30,8 @@ type RenderValueArg = { export const MultiSelectInput: React.FC = ({ className, onUpdate, + onInputBlur, + onInputFocus, value = [], placeholder, endContent, @@ -77,6 +81,8 @@ export const MultiSelectInput: React.FC = ({ (), AclSubjectSuggest: makeDefaultEmpty(), + AclSubjectSelect: makeDefaultEmpty(), DialogShare: makeDefaultEmpty(), DialogImageWidgetLinkHint: makeDefaultEmpty(), DialogRelatedEntitiesRadioHint: makeDefaultEmpty(), diff --git a/src/ui/registry/units/common/types/components/AclSubjectSelect.ts b/src/ui/registry/units/common/types/components/AclSubjectSelect.ts new file mode 100644 index 0000000000..e69275ff74 --- /dev/null +++ b/src/ui/registry/units/common/types/components/AclSubjectSelect.ts @@ -0,0 +1,29 @@ +import type {RefObject} from 'react'; +import type React from 'react'; + +import type {ListItemData} from '@gravity-ui/uikit'; + +import type { + ClaimsSubjectType, + SubjectClaims, +} from '../../../../../../shared/schema/extensions/types'; + +import type {AclSubjectSuggestProps} from './AclSubjectSuggest'; + +export type AclSubject = ListItemData; + +export type AclSelectedUser = { + id: string; + type: `${ClaimsSubjectType}`; + roles: string[]; + login: string; + avatarData?: string; +} & Omit; + +export interface AclSubjectSelectProps extends Omit { + inputRef?: RefObject; + value: AclSelectedUser[]; + onChange: (users: AclSelectedUser[]) => void; + size?: 'm' | 'l'; + endContent?: React.ReactNode; +} diff --git a/src/ui/registry/units/common/types/functions/useSubjectsListId.ts b/src/ui/registry/units/common/types/functions/useSubjectsListId.ts index 56de2fbc63..807bb4f009 100644 --- a/src/ui/registry/units/common/types/functions/useSubjectsListId.ts +++ b/src/ui/registry/units/common/types/functions/useSubjectsListId.ts @@ -1 +1,5 @@ -export type UseSubjectsListId = {type: 'organizationId' | 'cloudId'; id: string | undefined}; +export type UseSubjectsListId = { + type: 'organizationId' | 'cloudId'; + id: string | undefined; + title: string | undefined; +}; diff --git a/src/ui/units/workbooks/components/WorkbookActions/WorkbookActions.tsx b/src/ui/units/workbooks/components/WorkbookActions/WorkbookActions.tsx index 3f3b848e9d..ce98ac8765 100644 --- a/src/ui/units/workbooks/components/WorkbookActions/WorkbookActions.tsx +++ b/src/ui/units/workbooks/components/WorkbookActions/WorkbookActions.tsx @@ -193,14 +193,17 @@ export const WorkbookActions: React.FC = ({workbook, refreshWorkbookInfo} dropdownActions.push([...otherActions]); } - const {openAccessDialog} = registry.common.functions.getAll(); + const {openAccessDialog, useSubjectsListId} = registry.common.functions.getAll(); + + const {id: organizationId} = useSubjectsListId(); const onOpenAccessDialog = React.useCallback(() => { //Access list TODO this is draft only dispatch( openAccessDialog({ - resourceId: workbook.workbookId, - resourceType: ResourceType.Workbook, + workbookId: workbook.workbookId, + collectionId: workbook.collectionId, + organizationId, resourceTitle: workbook.title, canUpdate: workbook.permissions.updateAccessBindings, }), From a25d23bd4f5999e28b82f4aa98831a720eefa43a Mon Sep 17 00:00:00 2001 From: YankinA Date: Thu, 6 Nov 2025 15:15:09 +0300 Subject: [PATCH 05/25] add new access roles translations --- src/i18n-keysets/component.iam-access-dialog/en.json | 1 + src/i18n-keysets/component.iam-access-dialog/ru.json | 1 + 2 files changed, 2 insertions(+) diff --git a/src/i18n-keysets/component.iam-access-dialog/en.json b/src/i18n-keysets/component.iam-access-dialog/en.json index 5dee874223..8cc6f839db 100644 --- a/src/i18n-keysets/component.iam-access-dialog/en.json +++ b/src/i18n-keysets/component.iam-access-dialog/en.json @@ -35,6 +35,7 @@ "section_inherited_accesses": "Inherited permissions", "section_revoke-access": "Are you sure you want to revoke this role from {{userName}}? The user will lose access to all objects located in {{objectName}}.", "title_access-list": "Manage access", + "title_access-roles": "Access roles", "title_add-user": "Add user", "placeholder_select-option": "Not selected", "placeholder_select_subject_type": "Users and Groups", diff --git a/src/i18n-keysets/component.iam-access-dialog/ru.json b/src/i18n-keysets/component.iam-access-dialog/ru.json index dd7a6181f7..b3824c6853 100644 --- a/src/i18n-keysets/component.iam-access-dialog/ru.json +++ b/src/i18n-keysets/component.iam-access-dialog/ru.json @@ -35,6 +35,7 @@ "section_inherited_accesses": "Наследуемые права", "section_revoke-access": "Вы уверены, что хотите отозвать роль у {{userName}}? Пользователь потеряет доступ ко всем объектам находящимся в {{objectName}}.", "title_access-list": "Управление доступом", + "title_access-roles": "Список ролей", "title_add-user": "Добавление пользователя", "placeholder_select-option": "Не выбранно", "placeholder_select_subject_type": "Пользователи и группы", From 3d903e5672881fe22b47d87f687574c47aebcc3f Mon Sep 17 00:00:00 2001 From: YankinA Date: Fri, 7 Nov 2025 10:37:04 +0300 Subject: [PATCH 06/25] adding renderSubject prop for custom subject rendering --- .../registry/units/common/types/components/AclSubjectSelect.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ui/registry/units/common/types/components/AclSubjectSelect.ts b/src/ui/registry/units/common/types/components/AclSubjectSelect.ts index e69275ff74..91d2e5580c 100644 --- a/src/ui/registry/units/common/types/components/AclSubjectSelect.ts +++ b/src/ui/registry/units/common/types/components/AclSubjectSelect.ts @@ -22,6 +22,7 @@ export type AclSelectedUser = { export interface AclSubjectSelectProps extends Omit { inputRef?: RefObject; + renderSubject?: (subject: SubjectClaims) => React.ReactNode; value: AclSelectedUser[]; onChange: (users: AclSelectedUser[]) => void; size?: 'm' | 'l'; From ea355a9b05f68a90f8717f3351120ac115e1998c Mon Sep 17 00:00:00 2001 From: YankinA Date: Mon, 10 Nov 2025 01:12:33 +0300 Subject: [PATCH 07/25] add invite types --- src/shared/schema/extensions/actions/index.ts | 2 ++ .../actions/organization-manager.ts | 9 ++++++++ .../schema/extensions/types/invitations.ts | 22 +++++++++++++++++++ 3 files changed, 33 insertions(+) create mode 100644 src/shared/schema/extensions/actions/organization-manager.ts create mode 100644 src/shared/schema/extensions/types/invitations.ts diff --git a/src/shared/schema/extensions/actions/index.ts b/src/shared/schema/extensions/actions/index.ts index 01e3d36122..ffeeeba8ed 100644 --- a/src/shared/schema/extensions/actions/index.ts +++ b/src/shared/schema/extensions/actions/index.ts @@ -1,5 +1,7 @@ import {iamAccessDialogActions} from './iam-access-dialog'; +import {organizationManager} from './organization-manager'; export const actions = { ...iamAccessDialogActions, + ...organizationManager, }; diff --git a/src/shared/schema/extensions/actions/organization-manager.ts b/src/shared/schema/extensions/actions/organization-manager.ts new file mode 100644 index 0000000000..94ed0b5711 --- /dev/null +++ b/src/shared/schema/extensions/actions/organization-manager.ts @@ -0,0 +1,9 @@ +import {createAction} from '../../gateway-utils'; +import {GetDatalensOperationResponse} from '../../types'; +import {InviteUsersRequest} from '../types/invitations'; + +export const organizationManager = { + createUserInvite: createAction(async () => { + return {} as GetDatalensOperationResponse; + }), +}; diff --git a/src/shared/schema/extensions/types/invitations.ts b/src/shared/schema/extensions/types/invitations.ts new file mode 100644 index 0000000000..e50b8a1d50 --- /dev/null +++ b/src/shared/schema/extensions/types/invitations.ts @@ -0,0 +1,22 @@ +import {ResourceType, SubjectClaims} from './iam-access-dialog'; + +export type InviteUsersRequest = { + invites: Invite[]; +}; + +export type InviteUsersWithAccessRequest = InviteUsersRequest & { + resourceType: ResourceType; + resourceId: string; + roleId: string; +}; + +export type InviteUsersResponse = { + validInvites?: Invite[]; + invalidInvites?: Invite[]; +}; + +export type Invite = { + invitee: { + email: string; + }; +}; From 04f287246c6a606773e0f65bcebd8910058730a1 Mon Sep 17 00:00:00 2001 From: YankinA Date: Mon, 10 Nov 2025 18:34:58 +0300 Subject: [PATCH 08/25] refactor organization manager actions and update invitation types --- .../actions/organization-manager.ts | 7 +++++-- .../schema/extensions/types/invitations.ts | 21 ++++++++++++------- .../types/components/AclSubjectSelect.ts | 1 + 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/shared/schema/extensions/actions/organization-manager.ts b/src/shared/schema/extensions/actions/organization-manager.ts index 94ed0b5711..e89f6785a7 100644 --- a/src/shared/schema/extensions/actions/organization-manager.ts +++ b/src/shared/schema/extensions/actions/organization-manager.ts @@ -1,9 +1,12 @@ import {createAction} from '../../gateway-utils'; -import {GetDatalensOperationResponse} from '../../types'; +import {GetDatalensOperationArgs, GetDatalensOperationResponse} from '../../types'; import {InviteUsersRequest} from '../types/invitations'; export const organizationManager = { - createUserInvite: createAction(async () => { + createInvitation: createAction(async () => { + return {} as GetDatalensOperationResponse; + }), + getOperation: createAction(async () => { return {} as GetDatalensOperationResponse; }), }; diff --git a/src/shared/schema/extensions/types/invitations.ts b/src/shared/schema/extensions/types/invitations.ts index e50b8a1d50..3ae33a004f 100644 --- a/src/shared/schema/extensions/types/invitations.ts +++ b/src/shared/schema/extensions/types/invitations.ts @@ -1,7 +1,13 @@ -import {ResourceType, SubjectClaims} from './iam-access-dialog'; +import {ResourceType} from './iam-access-dialog'; + +export type Invite = { + invitee: { + email: string; + }; +}; export type InviteUsersRequest = { - invites: Invite[]; + invitations: Invite[]; }; export type InviteUsersWithAccessRequest = InviteUsersRequest & { @@ -11,12 +17,11 @@ export type InviteUsersWithAccessRequest = InviteUsersRequest & { }; export type InviteUsersResponse = { - validInvites?: Invite[]; - invalidInvites?: Invite[]; + validInvitations: InviteRespone[]; + invalidInvitations: InviteRespone[]; }; -export type Invite = { - invitee: { - email: string; - }; +export type InviteRespone = Invite & { + inviteeId: string; + inviterSubjectId: string; }; diff --git a/src/ui/registry/units/common/types/components/AclSubjectSelect.ts b/src/ui/registry/units/common/types/components/AclSubjectSelect.ts index 91d2e5580c..f523317bc7 100644 --- a/src/ui/registry/units/common/types/components/AclSubjectSelect.ts +++ b/src/ui/registry/units/common/types/components/AclSubjectSelect.ts @@ -21,6 +21,7 @@ export type AclSelectedUser = { } & Omit; export interface AclSubjectSelectProps extends Omit { + onKeyDown?: (input: HTMLInputElement, event: KeyboardEvent) => void; inputRef?: RefObject; renderSubject?: (subject: SubjectClaims) => React.ReactNode; value: AclSelectedUser[]; From efa2dabcc996a7a58faf1482f86aaa45c9dbccaa Mon Sep 17 00:00:00 2001 From: YankinA Date: Tue, 11 Nov 2025 09:52:05 +0300 Subject: [PATCH 09/25] add displayName property to SubjectClaims interface and export invitations type --- src/shared/schema/extensions/types/iam-access-dialog.ts | 1 + src/shared/schema/extensions/types/index.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/shared/schema/extensions/types/iam-access-dialog.ts b/src/shared/schema/extensions/types/iam-access-dialog.ts index 965352a1e0..23ffd913f1 100644 --- a/src/shared/schema/extensions/types/iam-access-dialog.ts +++ b/src/shared/schema/extensions/types/iam-access-dialog.ts @@ -109,6 +109,7 @@ export interface SubjectClaims { federation?: unknown; pictureData?: string; idpType?: string | null; + displayName?: string | React.ReactNode; } export type SubjectDetails = { diff --git a/src/shared/schema/extensions/types/index.ts b/src/shared/schema/extensions/types/index.ts index 38b9d8be75..ac36c4e3c6 100644 --- a/src/shared/schema/extensions/types/index.ts +++ b/src/shared/schema/extensions/types/index.ts @@ -1 +1,2 @@ export * from './iam-access-dialog'; +export * from './invitations'; From 6c025f5dc64e452d3639a61fddb84953d3b62ce4 Mon Sep 17 00:00:00 2001 From: YankinA Date: Tue, 11 Nov 2025 18:26:17 +0300 Subject: [PATCH 10/25] implement invitation management features including resend and delete actions, update access dialog translations, and enhance invitation types --- .../component.iam-access-dialog/en.json | 6 ++- .../component.iam-access-dialog/ru.json | 6 ++- .../actions/organization-manager.ts | 24 +++++++++- .../schema/extensions/types/invitations.ts | 47 ++++++++++++++++++- src/ui/components/AccessDialog/index.tsx | 20 ++++++++ .../registry/units/common/components-map.tsx | 3 +- src/ui/registry/units/common/functions-map.ts | 2 - .../common/types/components/AccessDialog.ts | 7 +++ src/ui/store/actions/openDialogTypes.ts | 4 +- .../WorkbookActions/WorkbookActions.tsx | 28 +++++------ 10 files changed, 123 insertions(+), 24 deletions(-) create mode 100644 src/ui/components/AccessDialog/index.tsx create mode 100644 src/ui/registry/units/common/types/components/AccessDialog.ts diff --git a/src/i18n-keysets/component.iam-access-dialog/en.json b/src/i18n-keysets/component.iam-access-dialog/en.json index 8cc6f839db..3300b4e3f6 100644 --- a/src/i18n-keysets/component.iam-access-dialog/en.json +++ b/src/i18n-keysets/component.iam-access-dialog/en.json @@ -41,5 +41,9 @@ "placeholder_select_subject_type": "Users and Groups", "label_search": "Search by name, title or email", "label_warning-workbook": "You are granting access to the workbook {{resourceTitle}} and all objects inside", - "label_warning-collection": "You are granting access to the collection {{resourceTitle}} and all objects inside" + "label_warning-collection": "You are granting access to the collection {{resourceTitle}} and all objects inside", + "title_invitation-sending": "Invitation sent to organization", + "action_resend-invitation": "Resend invitation", + "action_delete-invitation": "Delete", + "label_show-all-roles": "Show all roles" } diff --git a/src/i18n-keysets/component.iam-access-dialog/ru.json b/src/i18n-keysets/component.iam-access-dialog/ru.json index b3824c6853..bbca801934 100644 --- a/src/i18n-keysets/component.iam-access-dialog/ru.json +++ b/src/i18n-keysets/component.iam-access-dialog/ru.json @@ -41,5 +41,9 @@ "placeholder_select_subject_type": "Пользователи и группы", "label_search": "Искать по имени, названию или почте", "label_warning-workbook": "Вы даете доступ к воркбуку {{resourceTitle}} и всем объектам внутри", - "label_warning-collection": "Вы даете доступ к коллекции {{resourceTitle}} и всем объектам внутри" + "label_warning-collection": "Вы даете доступ к коллекции {{resourceTitle}} и всем объектам внутри", + "title_invitation-sending": "Приглашение отправлено в огранизацию", + "action_resend-invitation": "Отправить повторно", + "action_delete-invitation": "Удалить", + "label_show-all-roles": "Показать все роли" } diff --git a/src/shared/schema/extensions/actions/organization-manager.ts b/src/shared/schema/extensions/actions/organization-manager.ts index e89f6785a7..bfe9e6afe4 100644 --- a/src/shared/schema/extensions/actions/organization-manager.ts +++ b/src/shared/schema/extensions/actions/organization-manager.ts @@ -1,11 +1,33 @@ import {createAction} from '../../gateway-utils'; import {GetDatalensOperationArgs, GetDatalensOperationResponse} from '../../types'; -import {InviteUsersRequest} from '../types/invitations'; +import { + DeleteInvitationRequest, + InviteUsersRequest, + ListOrganizationInvitationsRequest, + ListOrganizationInvitationsResponse, + ResendInvitationRequest, +} from '../types/invitations'; export const organizationManager = { createInvitation: createAction(async () => { return {} as GetDatalensOperationResponse; }), + deleteInvitation: createAction( + async () => { + return {} as GetDatalensOperationResponse; + }, + ), + resendInvitation: createAction( + async () => { + return {} as GetDatalensOperationResponse; + }, + ), + listForOrganizationInvitations: createAction< + ListOrganizationInvitationsResponse, + ListOrganizationInvitationsRequest + >(async () => { + return {} as ListOrganizationInvitationsResponse; + }), getOperation: createAction(async () => { return {} as GetDatalensOperationResponse; }), diff --git a/src/shared/schema/extensions/types/invitations.ts b/src/shared/schema/extensions/types/invitations.ts index 3ae33a004f..e37dab95b8 100644 --- a/src/shared/schema/extensions/types/invitations.ts +++ b/src/shared/schema/extensions/types/invitations.ts @@ -22,6 +22,51 @@ export type InviteUsersResponse = { }; export type InviteRespone = Invite & { + invitationId: string; + status: InviteStatus; + createdAt: string; inviteeId: string; - inviterSubjectId: string; + subjectId: string; }; + +export type DeleteInvitationRequest = { + invitationId: string; +}; + +export enum InviteStatus { + Creating = 'CREATING', + Pending = 'PENDING', + Accepted = 'ACCEPTED', + Rejected = 'REJECTED', +} + +export type ListOrganizationInvitationsRequest = { + status: InviteStatus; + filter?: string | undefined; + pageSize?: number | undefined; + pageToken?: string; +}; + +export type ListOrganizationInvitationsResponse = { + invitations: Invitation[]; + nextPageToken: string; +}; + +export type Invitation = Invite & { + id: string; + status: InviteStatus; + inviteeId: string; + subjectId: string; +}; + +export interface ResendInvitationRequest { + invitationId: string; + notAfter?: InvitationTimestamp; +} +export interface ResendInvitationResponse { + invitationId: string; + organizationId: string; + email: string; +} + +export type InvitationTimestamp = {seconds: string; nanos?: number}; diff --git a/src/ui/components/AccessDialog/index.tsx b/src/ui/components/AccessDialog/index.tsx new file mode 100644 index 0000000000..01d62ec586 --- /dev/null +++ b/src/ui/components/AccessDialog/index.tsx @@ -0,0 +1,20 @@ +import React from 'react'; + +import {registry} from 'ui/registry'; + +import DialogManager from '../DialogManager/DialogManager'; +import {AccessDialogProps} from 'ui/registry/units/common/types/components/AccessDialog'; + +export const DIALOG_ACCESS = Symbol('DIALOG_ACCESS'); + +export type OpenDialogAccessDialogArgs = { + id: typeof DIALOG_ACCESS; + props: AccessDialogProps; +}; + +export const AccessDialogWrapper = (props: AccessDialogProps) => { + const {AccessDialog} = registry.common.components.getAll(); + return ; +}; + +DialogManager.registerDialog(DIALOG_ACCESS, AccessDialogWrapper); diff --git a/src/ui/registry/units/common/components-map.tsx b/src/ui/registry/units/common/components-map.tsx index 8353e2c8ae..058cae420a 100644 --- a/src/ui/registry/units/common/components-map.tsx +++ b/src/ui/registry/units/common/components-map.tsx @@ -32,6 +32,7 @@ import type {PlaceholderIllustrationImageProps} from './types/components/Placeho import type {ReportButtonProps} from './types/components/ReportButton'; import type {UserAvatarByIdProps} from './types/components/UserAvatarById'; import type {YfmWrapperProps} from './types/components/YfmWrapper'; +import {AccessDialogProps} from './types/components/AccessDialog'; export const commonComponentsMap = { [EXAMPLE_COMPONENT]: Example, @@ -65,5 +66,5 @@ export const commonComponentsMap = { DialogRelatedEntitiesRadioHint: makeDefaultEmpty(), WorkbookEntriesTableTabs: makeDefaultEmpty(), DialogEntryDescription: makeDefaultEmpty(), - AccessDialog: makeDefaultEmpty(), + AccessDialog: makeDefaultEmpty(), } as const; diff --git a/src/ui/registry/units/common/functions-map.ts b/src/ui/registry/units/common/functions-map.ts index 52111625b3..ad27a72df8 100644 --- a/src/ui/registry/units/common/functions-map.ts +++ b/src/ui/registry/units/common/functions-map.ts @@ -50,7 +50,6 @@ import type {GetLogoIcon} from './types/functions/getLogoIcon'; import type {GetShouldShowAIAssistant} from './types/functions/getShouldShowAIAssistant'; import type {GetUIEntryRouteArgs} from './types/functions/getUIEntryRoute'; import type {IsValidLogoUrl} from './types/functions/isValidLogoUrl'; -import type {OpenAccessDialog} from './types/functions/onOpenAcceessDialog'; import type {OpenDialogOrganizationInvite} from './types/functions/openDialogOrganizationInvite'; import type {OpenDialogOrganizationInviteUsers} from './types/functions/openDialogOrganizationInviteUsers'; import type {RenderDialogRelatedEntitiesAlertHint} from './types/functions/renderDialogRelatedEntitiesAlertHint'; @@ -179,5 +178,4 @@ export const commonFunctionsMap = { getLogoIcon: makeFunctionTemplate(), isValidLogoUrl: makeFunctionTemplate(), getShouldShowAIAssistant: makeFunctionTemplate(), - openAccessDialog: makeFunctionTemplate(), } as const; diff --git a/src/ui/registry/units/common/types/components/AccessDialog.ts b/src/ui/registry/units/common/types/components/AccessDialog.ts new file mode 100644 index 0000000000..f9b74f1fc9 --- /dev/null +++ b/src/ui/registry/units/common/types/components/AccessDialog.ts @@ -0,0 +1,7 @@ +export type AccessDialogProps = { + workbookId?: string; + collectionId?: string; + resourceTitle?: string; + canUpdate: boolean; + onClose?: () => void; +}; diff --git a/src/ui/store/actions/openDialogTypes.ts b/src/ui/store/actions/openDialogTypes.ts index 7ac5957f2f..9e83e991fb 100644 --- a/src/ui/store/actions/openDialogTypes.ts +++ b/src/ui/store/actions/openDialogTypes.ts @@ -59,6 +59,7 @@ import type {OpenDialogExportWorkbookArgs} from 'ui/components/CollectionsStruct import type {OpenDialogDefaultArgs} from 'ui/components/DialogDefault/DialogDefault'; import type {OpenDialogCreatePublicGalleryWorkbookArgs} from 'ui/components/CollectionsStructure/CreatePublicGalleryWorkbookDialog'; import type {OpenDialogEntryDescriptionArgs} from 'ui/components/DialogEntryDescription/DialogEntryDescriptionWrapper'; +import type {OpenDialogAccessDialogArgs} from 'ui/components/AccessDialog'; export type OpenDialogArgs = | OpenDialogReleaseVersionArgs @@ -119,4 +120,5 @@ export type OpenDialogArgs = | OpenDialogExportWorkbookArgs | OpenDialogDefaultArgs | OpenDialogCreatePublicGalleryWorkbookArgs - | OpenDialogEntryDescriptionArgs; + | OpenDialogEntryDescriptionArgs + | OpenDialogAccessDialogArgs; diff --git a/src/ui/units/workbooks/components/WorkbookActions/WorkbookActions.tsx b/src/ui/units/workbooks/components/WorkbookActions/WorkbookActions.tsx index ce98ac8765..f363724830 100644 --- a/src/ui/units/workbooks/components/WorkbookActions/WorkbookActions.tsx +++ b/src/ui/units/workbooks/components/WorkbookActions/WorkbookActions.tsx @@ -25,6 +25,7 @@ import {IamAccessDialog} from '../../../../components/IamAccessDialog/IamAccessD import {registry} from '../../../../registry'; import {ResourceType} from '../../../../registry/units/common/types/components/IamAccessDialog'; import {CreateEntry} from '../CreateEntry/CreateEntry'; +import {DIALOG_ACCESS} from 'ui/components/AccessDialog'; import './WorkbookActions.scss'; @@ -193,26 +194,23 @@ export const WorkbookActions: React.FC = ({workbook, refreshWorkbookInfo} dropdownActions.push([...otherActions]); } - const {openAccessDialog, useSubjectsListId} = registry.common.functions.getAll(); - - const {id: organizationId} = useSubjectsListId(); - const onOpenAccessDialog = React.useCallback(() => { - //Access list TODO this is draft only dispatch( - openAccessDialog({ - workbookId: workbook.workbookId, - collectionId: workbook.collectionId, - organizationId, - resourceTitle: workbook.title, - canUpdate: workbook.permissions.updateAccessBindings, + openDialog({ + id: DIALOG_ACCESS, + props: { + workbookId: workbook?.workbookId ?? undefined, + collectionId: workbook?.collectionId ?? undefined, + resourceTitle: workbook.title, + canUpdate: workbook.permissions.updateAccessBindings, + onClose: () => { + dispatch(closeDialog()); + }, + }, }), ); - // setIamAccessDialogIsOpen(true); }, []); - const {AccessDialog} = registry.common.components.getAll(); - return (
{Boolean(dropdownActions.length) && ( @@ -245,8 +243,6 @@ export const WorkbookActions: React.FC = ({workbook, refreshWorkbookInfo}
)} - - {collectionsAccessEnabled && workbook.permissions.listAccessBindings && ( Date: Wed, 12 Nov 2025 11:38:03 +0300 Subject: [PATCH 11/25] add EnableNewAccessDialog feature and integrate into useActions --- .../features-list/EnableNewAccessDialog.ts | 10 ++++ src/shared/types/feature.ts | 3 ++ .../CollectionContent/hooks/useActions.tsx | 49 +++++++++++++------ 3 files changed, 47 insertions(+), 15 deletions(-) create mode 100644 src/server/components/features/features-list/EnableNewAccessDialog.ts diff --git a/src/server/components/features/features-list/EnableNewAccessDialog.ts b/src/server/components/features/features-list/EnableNewAccessDialog.ts new file mode 100644 index 0000000000..a70acf030f --- /dev/null +++ b/src/server/components/features/features-list/EnableNewAccessDialog.ts @@ -0,0 +1,10 @@ +import {Feature} from '../../../../shared'; +import {createFeatureConfig} from '../utils'; + +export default createFeatureConfig({ + name: Feature.EnableNewAccessDialog, + state: { + development: false, + production: false, + }, +}); diff --git a/src/shared/types/feature.ts b/src/shared/types/feature.ts index b6575fc409..3d0c426e62 100644 --- a/src/shared/types/feature.ts +++ b/src/shared/types/feature.ts @@ -97,6 +97,9 @@ export enum Feature { EnableConnectionDescription = 'EnableConnectionDescription', EnableMobileFixedHeader = 'EnableMobileFixedHeader', + + /** Enable new access dialog (AccessDialog) */ + EnableNewAccessDialog = 'EnableNewAccessDialog', } export type FeatureConfig = Record; diff --git a/src/ui/units/collections/components/CollectionContent/hooks/useActions.tsx b/src/ui/units/collections/components/CollectionContent/hooks/useActions.tsx index 811d178f58..a140912f64 100644 --- a/src/ui/units/collections/components/CollectionContent/hooks/useActions.tsx +++ b/src/ui/units/collections/components/CollectionContent/hooks/useActions.tsx @@ -33,6 +33,7 @@ import type {AppDispatch} from '../../../../../store'; import {closeDialog, openDialog} from '../../../../../store/actions/dialog'; import {WORKBOOKS_PATH} from '../../../../collections-navigation/constants'; import {deleteCollectionInItems, deleteWorkbookInItems} from '../../../store/actions'; +import {DIALOG_ACCESS} from 'ui/components/AccessDialog'; const i18n = I18n.keyset('collections'); @@ -285,25 +286,43 @@ export const useActions = ({fetchStructureItems, onCloseMoveDialog}: UseActionsA } if (collectionsAccessEnabled && item.permissions.listAccessBindings) { + const isNewAccessDialogEnabled = isEnabledFeature(Feature.EnableNewAccessDialog); actions.push({ text: , action: () => { - dispatch( - openDialog({ - id: DIALOG_IAM_ACCESS, - props: { - open: true, - resourceId: item.workbookId, - resourceType: ResourceType.Workbook, - resourceTitle: item.title, - parentId: item.collectionId, - canUpdate: item.permissions.updateAccessBindings, - onClose: () => { - dispatch(closeDialog()); + if (isNewAccessDialogEnabled) { + dispatch( + openDialog({ + id: DIALOG_ACCESS, + props: { + workbookId: item?.workbookId ?? undefined, + collectionId: item?.collectionId ?? undefined, + resourceTitle: item?.title, + canUpdate: item?.permissions.updateAccessBindings, + onClose: () => { + dispatch(closeDialog()); + }, }, - }, - }), - ); + }), + ); + } else { + dispatch( + openDialog({ + id: DIALOG_IAM_ACCESS, + props: { + open: true, + resourceId: item.workbookId, + resourceType: ResourceType.Workbook, + resourceTitle: item.title, + parentId: item.collectionId, + canUpdate: item.permissions.updateAccessBindings, + onClose: () => { + dispatch(closeDialog()); + }, + }, + }), + ); + } }, }); } From e03605e7f4798f827ef7dcb4eee894b0284691af Mon Sep 17 00:00:00 2001 From: YankinA Date: Thu, 13 Nov 2025 10:12:49 +0300 Subject: [PATCH 12/25] Update locale and add updateAccessBindings action to organization manager. --- src/i18n-keysets/component.iam-access-dialog/en.json | 2 +- src/i18n-keysets/component.iam-access-dialog/ru.json | 2 +- .../schema/extensions/actions/organization-manager.ts | 6 ++++++ src/shared/schema/extensions/types/iam-access-dialog.ts | 4 ++++ .../components/WorkbookActions/WorkbookActions.tsx | 6 +++++- 5 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/i18n-keysets/component.iam-access-dialog/en.json b/src/i18n-keysets/component.iam-access-dialog/en.json index 3300b4e3f6..556629c9f9 100644 --- a/src/i18n-keysets/component.iam-access-dialog/en.json +++ b/src/i18n-keysets/component.iam-access-dialog/en.json @@ -42,7 +42,7 @@ "label_search": "Search by name, title or email", "label_warning-workbook": "You are granting access to the workbook {{resourceTitle}} and all objects inside", "label_warning-collection": "You are granting access to the collection {{resourceTitle}} and all objects inside", - "title_invitation-sending": "Invitation sent to organization", + "title_invitation-sending": "Invitation to organization", "action_resend-invitation": "Resend invitation", "action_delete-invitation": "Delete", "label_show-all-roles": "Show all roles" diff --git a/src/i18n-keysets/component.iam-access-dialog/ru.json b/src/i18n-keysets/component.iam-access-dialog/ru.json index bbca801934..789e509f53 100644 --- a/src/i18n-keysets/component.iam-access-dialog/ru.json +++ b/src/i18n-keysets/component.iam-access-dialog/ru.json @@ -42,7 +42,7 @@ "label_search": "Искать по имени, названию или почте", "label_warning-workbook": "Вы даете доступ к воркбуку {{resourceTitle}} и всем объектам внутри", "label_warning-collection": "Вы даете доступ к коллекции {{resourceTitle}} и всем объектам внутри", - "title_invitation-sending": "Приглашение отправлено в огранизацию", + "title_invitation-sending": "Приглашение в огранизацию", "action_resend-invitation": "Отправить повторно", "action_delete-invitation": "Удалить", "label_show-all-roles": "Показать все роли" diff --git a/src/shared/schema/extensions/actions/organization-manager.ts b/src/shared/schema/extensions/actions/organization-manager.ts index bfe9e6afe4..be9112b1aa 100644 --- a/src/shared/schema/extensions/actions/organization-manager.ts +++ b/src/shared/schema/extensions/actions/organization-manager.ts @@ -1,5 +1,6 @@ import {createAction} from '../../gateway-utils'; import {GetDatalensOperationArgs, GetDatalensOperationResponse} from '../../types'; +import {UpdateAccessBindingsRequest} from '../types'; import { DeleteInvitationRequest, InviteUsersRequest, @@ -28,6 +29,11 @@ export const organizationManager = { >(async () => { return {} as ListOrganizationInvitationsResponse; }), + updateAccessBindings: createAction( + async () => { + return {} as GetDatalensOperationResponse; + }, + ), getOperation: createAction(async () => { return {} as GetDatalensOperationResponse; }), diff --git a/src/shared/schema/extensions/types/iam-access-dialog.ts b/src/shared/schema/extensions/types/iam-access-dialog.ts index 23ffd913f1..371c49ce02 100644 --- a/src/shared/schema/extensions/types/iam-access-dialog.ts +++ b/src/shared/schema/extensions/types/iam-access-dialog.ts @@ -168,3 +168,7 @@ export enum ResourceType { Collection = 'collection', Workbook = 'workbook', } + +export type UpdateAccessBindingsRequest = { + accessBindingDeltas: AccessBindingDelta[]; +}; diff --git a/src/ui/units/workbooks/components/WorkbookActions/WorkbookActions.tsx b/src/ui/units/workbooks/components/WorkbookActions/WorkbookActions.tsx index f363724830..611ba814b3 100644 --- a/src/ui/units/workbooks/components/WorkbookActions/WorkbookActions.tsx +++ b/src/ui/units/workbooks/components/WorkbookActions/WorkbookActions.tsx @@ -228,7 +228,11 @@ export const WorkbookActions: React.FC = ({workbook, refreshWorkbookInfo}