diff --git a/src/shared/schema/us/actions/entries/index.ts b/src/shared/schema/us/actions/entries/index.ts index 3fbcf85d06..81e3efadfd 100644 --- a/src/shared/schema/us/actions/entries/index.ts +++ b/src/shared/schema/us/actions/entries/index.ts @@ -44,6 +44,8 @@ import type { GetRevisionsResponse, GetSharedEntryBindingsArgs, GetSharedEntryBindingsResponse, + GetSharedEntryWorkbookRelationsArgs, + GetSharedEntryWorkbookRelationsResponse, MoveEntryArgs, MoveEntryResponse, RenameEntryArgs, @@ -303,4 +305,18 @@ export const entriesActions = { headers, }), }), + getSharedEntryWorkbookRelations: createAction< + GetSharedEntryWorkbookRelationsResponse, + GetSharedEntryWorkbookRelationsArgs + >({ + method: 'GET', + path: ({entryId}) => `${PATH_PREFIX}/shared-entries/${entryId}/workbook-relations`, + params: ({scope, workbookId}, headers) => ({ + query: { + workbookId, + scope, + }, + headers, + }), + }), }; diff --git a/src/shared/schema/us/types/entries.ts b/src/shared/schema/us/types/entries.ts index 6644700806..3ca7a0a5fa 100644 --- a/src/shared/schema/us/types/entries.ts +++ b/src/shared/schema/us/types/entries.ts @@ -21,6 +21,7 @@ import type { EntryNavigationFields, EntryRelationFields, SharedEntryPermissions, + SharedEntryRelationFields, } from './fields'; export interface GetEntryResponse extends EntryFields { @@ -313,3 +314,13 @@ export type SharedEntryBindingsItem = { export type GetSharedEntryBindingsResponse = { items: SharedEntryBindingsItem[]; }; + +export type GetSharedEntryWorkbookRelationsArgs = { + entryId: string; + workbookId: string; + scope?: `${EntryScope}`; +}; + +export type GetSharedEntryWorkbookRelationsResponse = { + relations: SharedEntryRelationFields[]; +}; diff --git a/src/shared/schema/us/types/fields.ts b/src/shared/schema/us/types/fields.ts index 0139e3963d..76fa3a8ce9 100644 --- a/src/shared/schema/us/types/fields.ts +++ b/src/shared/schema/us/types/fields.ts @@ -132,6 +132,11 @@ export interface EntryRelationFields { isLocked: boolean; } +export type SharedEntryRelationFields = Omit & { + collectionId: string; + createdAt: string; +}; + export interface TenantSettings { defaultColorPaletteId?: string; } diff --git a/src/ui/components/DialogSharedEntryBindings/DialogSharedEntryBindings.scss b/src/ui/components/DialogSharedEntryBindings/DialogSharedEntryBindings.scss index 1e01a66fc1..422339a2ab 100644 --- a/src/ui/components/DialogSharedEntryBindings/DialogSharedEntryBindings.scss +++ b/src/ui/components/DialogSharedEntryBindings/DialogSharedEntryBindings.scss @@ -47,8 +47,6 @@ } &__footer { - margin-left: -32px; - margin-right: -32px; border-top: 1px solid var(--g-color-line-generic); &_empty-list { border: none; diff --git a/src/ui/components/DialogSharedEntryBindings/DialogSharedEntryBindings.tsx b/src/ui/components/DialogSharedEntryBindings/DialogSharedEntryBindings.tsx index ab3efa5587..2af040fd40 100644 --- a/src/ui/components/DialogSharedEntryBindings/DialogSharedEntryBindings.tsx +++ b/src/ui/components/DialogSharedEntryBindings/DialogSharedEntryBindings.tsx @@ -53,8 +53,11 @@ export const DialogSharedEntryBindings: React.FC onDirectionChange, currentDirection, fetchEntityBindings, + isLoadingDelete, + onDelete, } = useSharedEntryBindings({ entry, + onDeleteSuccess, }); const showDirectionControl = entry.scope === 'dataset'; @@ -71,11 +74,12 @@ export const DialogSharedEntryBindings: React.FC title={getSharedEntryMockText('label-current-entry')} className={b('current-row')} /> - {isLoading && !isSearchLoading ? ( + {(isLoading || isLoadingDelete) && !isSearchLoading ? ( ) : ( <> /> )} - {isDeleteDialog && ( - fetchEntityBindings(searchValue)} - isLoading={isLoading || isSearchLoading} - emptyList={entities.length === 0} - onDeleteSuccess={onDeleteSuccess} - /> - )} + {isDeleteDialog && ( + fetchEntityBindings(searchValue)} + isLoading={isLoading || isSearchLoading || isLoadingDelete} + emptyList={entities.length === 0} + onDelete={onDelete} + /> + )} ); }; diff --git a/src/ui/components/DialogSharedEntryBindings/components/DeleteAlert.tsx b/src/ui/components/DialogSharedEntryBindings/components/DeleteAlert.tsx index ac7790e873..71ac33d421 100644 --- a/src/ui/components/DialogSharedEntryBindings/components/DeleteAlert.tsx +++ b/src/ui/components/DialogSharedEntryBindings/components/DeleteAlert.tsx @@ -15,12 +15,19 @@ type DeleteAlertProps = { entities: SharedEntryBindingsItem[]; isDeleteDialog: boolean; isError: boolean; + isSearchActive: boolean; }; const b = block(DialogClassName); -export const DeleteAlert = ({entities, entry, isDeleteDialog, isError}: DeleteAlertProps) => { - if (!isDeleteDialog || isError) { +export const DeleteAlert = ({ + entities, + entry, + isDeleteDialog, + isError, + isSearchActive, +}: DeleteAlertProps) => { + if (!isDeleteDialog || isError || (isSearchActive && entities.length === 0)) { return null; } diff --git a/src/ui/components/DialogSharedEntryBindings/components/Relations.tsx b/src/ui/components/DialogSharedEntryBindings/components/Relations.tsx index 6e47469d4d..53b244be56 100644 --- a/src/ui/components/DialogSharedEntryBindings/components/Relations.tsx +++ b/src/ui/components/DialogSharedEntryBindings/components/Relations.tsx @@ -174,7 +174,7 @@ export const Relations = ({ ); } - if (isDeleteDialog && entities.length === 0) { + if (isDeleteDialog && entities.length === 0 && searchValue === '') { return null; } diff --git a/src/ui/components/DialogSharedEntryBindings/components/SharedBindingsFooter.tsx b/src/ui/components/DialogSharedEntryBindings/components/SharedBindingsFooter.tsx index e4b667c774..df8881506e 100644 --- a/src/ui/components/DialogSharedEntryBindings/components/SharedBindingsFooter.tsx +++ b/src/ui/components/DialogSharedEntryBindings/components/SharedBindingsFooter.tsx @@ -2,14 +2,9 @@ import React from 'react'; import {Button, Dialog, Icon} from '@gravity-ui/uikit'; import block from 'bem-cn-lite'; -import {useDispatch} from 'react-redux'; -import {getSdk} from 'ui/libs/schematic-sdk'; -import type {AppDispatch} from 'ui/store'; -import {showToast} from 'ui/store/actions/toaster'; import {getSharedEntryMockText} from 'ui/units/collections/components/helpers'; import {DialogClassName} from '../constants'; -import type {SharedEntry} from '../types'; import ArrowsRotateRightIcon from '@gravity-ui/icons/svgs/arrows-rotate-right.svg'; @@ -18,43 +13,18 @@ type SharedBindingsFooterProps = { onRefresh: () => void; onClose: () => void; emptyList: boolean; - entry: SharedEntry; - onDeleteSuccess?: () => void; + onDelete: () => void; }; const b = block(DialogClassName); export const SharedBindingsFooter = ({ - entry, isLoading, onRefresh, emptyList, onClose, - onDeleteSuccess, + onDelete, }: SharedBindingsFooterProps) => { - const dispatch: AppDispatch = useDispatch(); - const [isLoadingDelete, setIsLoadingDelete] = React.useState(false); - - const onDelete = React.useCallback(async () => { - setIsLoadingDelete(true); - try { - await getSdk().sdk.mix.deleteEntry({ - entryId: entry.entryId, - scope: entry.scope, - }); - setIsLoadingDelete(false); - onDeleteSuccess?.(); - } catch (error) { - setIsLoadingDelete(false); - dispatch( - showToast({ - title: error.message, - error, - }), - ); - } - }, [onDeleteSuccess, entry, dispatch]); - return ( - diff --git a/src/ui/components/DialogSharedEntryBindings/hooks/useSharedEntryBindings.ts b/src/ui/components/DialogSharedEntryBindings/hooks/useSharedEntryBindings.ts index fa6a01ea93..d1bb683956 100644 --- a/src/ui/components/DialogSharedEntryBindings/hooks/useSharedEntryBindings.ts +++ b/src/ui/components/DialogSharedEntryBindings/hooks/useSharedEntryBindings.ts @@ -1,21 +1,27 @@ import React from 'react'; import debounce from 'lodash/debounce'; +import {useDispatch} from 'react-redux'; import type {SharedEntryBindingsItem} from 'shared/schema'; import {getSdk} from 'ui/libs/schematic-sdk'; +import type {AppDispatch} from 'ui/store'; +import {showToast} from 'ui/store/actions/toaster'; import type {AttachmentValue} from '../constants'; import {Attachment, SEARCH_DELAY} from '../constants'; import type {SharedEntry} from '../types'; import {sortEntities} from '../utils'; + type UseSharedEntryBindingsProps = { entry: SharedEntry; + onDeleteSuccess?: () => void; }; const CONCURRENT_ID = 'shared-entry-bindings'; const cancelConcurrentRequest = () => getSdk().cancelRequest(CONCURRENT_ID); -export const useSharedEntryBindings = ({entry}: UseSharedEntryBindingsProps) => { +export const useSharedEntryBindings = ({entry, onDeleteSuccess}: UseSharedEntryBindingsProps) => { + const dispatch: AppDispatch = useDispatch(); const [entities, setEntities] = React.useState([]); const [currentDirection, setCurrentDirection] = React.useState( @@ -25,6 +31,27 @@ export const useSharedEntryBindings = ({entry}: UseSharedEntryBindingsProps) => const [isLoading, setIsLoading] = React.useState(true); const [isSearchLoading, setIsSearchLoading] = React.useState(false); const [isError, setIsError] = React.useState(false); + const [isLoadingDelete, setIsLoadingDelete] = React.useState(false); + + const onDelete = React.useCallback(async () => { + setIsLoadingDelete(true); + try { + await getSdk().sdk.mix.deleteEntry({ + entryId: entry.entryId, + scope: entry.scope, + }); + setIsLoadingDelete(false); + onDeleteSuccess?.(); + } catch (error) { + setIsLoadingDelete(false); + dispatch( + showToast({ + title: error.message, + error, + }), + ); + } + }, [onDeleteSuccess, entry, dispatch]); const fetchEntityBindings = React.useCallback( (filter = '') => { @@ -91,6 +118,8 @@ export const useSharedEntryBindings = ({entry}: UseSharedEntryBindingsProps) => return { isError, isLoading, + isLoadingDelete, + onDelete, isSearchLoading, onSearch, searchValue: searchFilter, diff --git a/src/ui/store/actions/openDialogTypes.ts b/src/ui/store/actions/openDialogTypes.ts index 54035f7a80..52846f2556 100644 --- a/src/ui/store/actions/openDialogTypes.ts +++ b/src/ui/store/actions/openDialogTypes.ts @@ -24,6 +24,7 @@ import type {OpenDialogColumnSettingsArgs} from '../../units/wizard/components/D import type {OpenDialogFieldEditorArgs} from '../../components/DialogFieldEditor/DialogFieldEditor'; import type {OpenDialogRenameEntryInNewWorkbookArgs} from '../../units/workbooks/components/RenameEntryDialog/RenameEntryDialog'; import type {OpenDialogDeleteEntryInNewWorkbookArgs} from '../../units/workbooks/components/DeleteEntryDialog/DeleteEntryDialog'; +import type {OpenDialogDeleteSharedEntryInWorkbookArgs} from '../../units/workbooks/components/DeleteSharedEntryDialog/DeleteSharedEntryDialog'; import type {OpenDialogDuplicateEntryInWorkbookArgs} from '../../units/workbooks/components/DuplicateEntryDialog/DuplicateEntryDialog'; import type {OpenDialogConnS3Sources} from '../../units/connections/components/dialogs'; import type {OpenDialogConnWithInputArgs} from '../../units/connections/components/custom-forms/components/DialogWithInput'; @@ -129,4 +130,5 @@ export type OpenDialogArgs = | OpenDialogSharedEntryUnbindArgs | OpenDialogSharedEntryPermissionsArgs | OpenDialogSelectSharedEntryArgs + | OpenDialogDeleteSharedEntryInWorkbookArgs | OpenDialogEntryDescriptionArgs; diff --git a/src/ui/units/collections/components/constants.ts b/src/ui/units/collections/components/constants.ts index 1ee857b78f..f374a6490d 100644 --- a/src/ui/units/collections/components/constants.ts +++ b/src/ui/units/collections/components/constants.ts @@ -37,13 +37,20 @@ export const mockSharedEntriesTexts = { 'apply-bindings-dialog-delete': 'Удалить', 'alert-title-info-bindings-dialog-delete': '{{entry}} нигде не используется', 'alert-message-info-bindings-dialog-delete': 'Удаление ничего не сломает', + 'alert-message-info-workbook-dialog-delete': + 'Удаление не повлияет на Wizard-чарты, но может повлиять на Editor-чарты если используется в них', 'alert-title-warning-bindings-dialog-delete': '{{entry}} используется в некоторых {{relation}}', 'alert-message-warning-bindings-dialog-delete': 'Перед удалением убедитесь, что ничего не сломается', + 'alert-message-warning-workbook-dialog-delete': + 'Перед удалением убедитесь, что ничего не сломается. Если у вас есть Editor-чарты, их придется проверить вручную', 'cancel-bindings-dialog-delete': 'Отмена', 'relations-bindings-dialog-delete': 'воркбуках и датасетах', + 'relations-workbook-dialog-delete': 'зависимостях', 'relation-workbook-bindings-dialog-delete': 'воркбуках', 'relation-dataset-bindings-dialog-delete': 'датасетах', + 'relation-chart-workbook-dialog-delete': 'чартах', + 'relation-dash-workbook-dialog-delete': 'дашбордах', 'bindings-dialog-delete-refresh-btn': 'Обновить', 'entries-list-title-workbook': 'Воркбуки', 'entries-list-title-connection': 'Подключения', diff --git a/src/ui/units/workbooks/components/DeleteSharedEntryDialog/DeleteSharedEntryDialog.scss b/src/ui/units/workbooks/components/DeleteSharedEntryDialog/DeleteSharedEntryDialog.scss new file mode 100644 index 0000000000..e933de98fa --- /dev/null +++ b/src/ui/units/workbooks/components/DeleteSharedEntryDialog/DeleteSharedEntryDialog.scss @@ -0,0 +1,37 @@ +.dl-shared-entry-workbook-delete-dialog { + max-height: 85vh; + + &__body { + display: flex; + flex-direction: column; + height: 100%; + } + + &__list { + margin-top: var(--g-spacing-4); + width: 100%; + display: flex; + flex-direction: column; + gap: var(--g-spacing-4); + overflow-y: auto; + } + + &__current-row { + margin-bottom: var(--g-spacing-4); + } + + &__error-state { + display: flex; + justify-content: center; + align-items: center; + width: 100%; + } + + &__button-retry { + margin-top: var(--g-spacing-5); + } + + &__info { + margin-bottom: var(--g-spacing-4); + } +} diff --git a/src/ui/units/workbooks/components/DeleteSharedEntryDialog/DeleteSharedEntryDialog.tsx b/src/ui/units/workbooks/components/DeleteSharedEntryDialog/DeleteSharedEntryDialog.tsx new file mode 100644 index 0000000000..396df599ef --- /dev/null +++ b/src/ui/units/workbooks/components/DeleteSharedEntryDialog/DeleteSharedEntryDialog.tsx @@ -0,0 +1,226 @@ +import React from 'react'; + +import {Alert, Button, Dialog, Icon} from '@gravity-ui/uikit'; +import block from 'bem-cn-lite'; +import isEmpty from 'lodash/isEmpty'; +import {useDispatch} from 'react-redux'; +import {EntryScope} from 'shared'; +import type {SharedEntryRelationFields} from 'shared/schema'; +import DialogManager from 'ui/components/DialogManager/DialogManager'; +import {EntitiesList} from 'ui/components/EntitiesList/EntitiesList'; +import {PlaceholderIllustration} from 'ui/components/PlaceholderIllustration/PlaceholderIllustration'; +import {SharedEntryIcon} from 'ui/components/SharedEntryIcon/SharedEntryIcon'; +import {SmartLoader} from 'ui/components/SmartLoader/SmartLoader'; +import {getSdk} from 'ui/libs/schematic-sdk'; +import type {AppDispatch} from 'ui/store'; +import {showToast} from 'ui/store/actions/toaster'; +import {getSharedEntryMockText} from 'ui/units/collections/components/helpers'; +import {groupEntitiesByScope} from 'ui/utils/helpers'; + +import type {WorkbookSharedEntry} from '../../types'; + +import ArrowsRotateRightIcon from '@gravity-ui/icons/svgs/arrows-rotate-right.svg'; + +import './DeleteSharedEntryDialog.scss'; + +export type Props = { + open: boolean; + entry: WorkbookSharedEntry; + workbookId: string; + onDeleteSuccess?: () => void; + onClose: () => void; +}; + +export const DIALOG_DELETE_SHARED_ENTRY_IN_WORKBOOK = Symbol( + 'DIALOG_DELETE_SHARED_ENTRY_IN_WORKBOOK', +); + +export type OpenDialogDeleteSharedEntryInWorkbookArgs = { + id: typeof DIALOG_DELETE_SHARED_ENTRY_IN_WORKBOOK; + props: Props; +}; + +type Entities = { + [K in EntryScope]?: SharedEntryRelationFields[]; +}; + +const b = block('dl-shared-entry-workbook-delete-dialog'); + +const CONCURRENT_ID = 'shared-entry-workbook-deleting'; +const cancelConcurrentRequest = () => getSdk().cancelRequest(CONCURRENT_ID); + +export const getRelationText = (entities: Entities | null) => { + const hasDataset = Boolean(entities?.[EntryScope.Dataset]); + const hasDash = Boolean(entities?.[EntryScope.Dash]); + const hasWidget = Boolean(entities?.[EntryScope.Widget]); + + if ([hasDash, hasDataset, hasWidget].filter(Boolean).length > 1) { + return getSharedEntryMockText('relations-workbook-dialog-delete'); + } else if (hasDataset) { + return getSharedEntryMockText('relation-dataset-bindings-dialog-delete'); + } else if (hasDash) { + return getSharedEntryMockText('relation-dash-workbook-dialog-delete'); + } else if (hasWidget) { + return getSharedEntryMockText('relation-chart-workbook-dialog-delete'); + } else { + return ''; + } +}; + +const DeleteSharedEntryDialog = React.memo( + ({open, entry, onClose, workbookId, onDeleteSuccess}) => { + const dispatch: AppDispatch = useDispatch(); + const [entities, setEntities] = React.useState(null); + const [isLoading, setIsLoading] = React.useState(true); + const [isLoadingDelete, setIsLoadingDelete] = React.useState(false); + const [isError, setIsError] = React.useState(false); + + const isListEmpty = isEmpty(entities); + const alertTheme = isListEmpty ? 'info' : 'warning'; + const alertTitle = getSharedEntryMockText( + isListEmpty + ? 'alert-title-info-bindings-dialog-delete' + : 'alert-title-warning-bindings-dialog-delete', + { + entry: getSharedEntryMockText(`label-shared-${entry.scope}`), + relation: getRelationText(entities), + }, + ); + const alertMessage = getSharedEntryMockText( + isListEmpty + ? 'alert-message-info-workbook-dialog-delete' + : 'alert-message-warning-workbook-dialog-delete', + ); + const fetchEntityRelations = React.useCallback(() => { + setIsLoading(true); + setIsError(false); + cancelConcurrentRequest(); + getSdk() + .sdk.us.getSharedEntryWorkbookRelations({ + entryId: entry.entryId, + workbookId, + }) + .then((response) => { + setEntities(groupEntitiesByScope(response.relations)); + setIsLoading(false); + }) + .catch((error) => { + if (error.isCancelled) { + return; + } + setIsError(true); + setIsLoading(false); + }); + }, [entry, workbookId]); + + const onDelete = React.useCallback(async () => { + setIsLoadingDelete(true); + try { + await getSdk().sdk.us.deleteSharedEntryBinding({ + sourceId: entry.entryId, + targetId: workbookId, + }); + setIsLoadingDelete(false); + onDeleteSuccess?.(); + } catch (error) { + setIsLoadingDelete(false); + dispatch( + showToast({ + title: error.message, + error, + }), + ); + } + }, [entry, workbookId, dispatch, onDeleteSuccess]); + + const renderRelations = () => { + if (isError) { + const renderRetryAction = () => ( + + ); + + return ( +
+ +
+ ); + } + + return Object.entries(entities ?? {}).map(([key, value]) => ( + + )); + }; + + React.useEffect(() => { + fetchEntityRelations(); + }, [fetchEntityRelations]); + + return ( + + + + } + /> + {isLoading ? ( + + ) : ( + <> + + {!isListEmpty &&
{renderRelations()}
} + + )} +
+ + + +
+ ); + }, +); + +DeleteSharedEntryDialog.displayName = 'DeleteSharedEntryDialog'; + +DialogManager.registerDialog(DIALOG_DELETE_SHARED_ENTRY_IN_WORKBOOK, DeleteSharedEntryDialog); diff --git a/src/ui/units/workbooks/components/EntryActions/EntryActions.tsx b/src/ui/units/workbooks/components/EntryActions/EntryActions.tsx index 21884074ae..df75db5a65 100644 --- a/src/ui/units/workbooks/components/EntryActions/EntryActions.tsx +++ b/src/ui/units/workbooks/components/EntryActions/EntryActions.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import {CodeTrunk, Copy, CopyArrowRight, FontCursor, TrashBin} from '@gravity-ui/icons'; +import {CodeTrunk, Copy, CopyArrowRight, FontCursor, Shield, TrashBin} from '@gravity-ui/icons'; import type {DropdownMenuItemMixed} from '@gravity-ui/uikit'; import {DropdownMenu} from '@gravity-ui/uikit'; import {I18n} from 'i18n'; @@ -10,6 +10,7 @@ import {WorkbookPageQa} from 'shared/constants/qa/workbooks'; import type {WorkbookWithPermissions} from 'shared/schema/us/types'; import {EntryScope} from 'shared/types/common'; import {S3_BASED_CONNECTORS} from 'ui/constants'; +import {getSharedEntryMockText} from 'ui/units/collections/components/helpers'; import {isEnabledFeature} from 'ui/utils/isEnabledFeature'; import {DropdownAction} from '../../../../components/DropdownAction/DropdownAction'; @@ -32,6 +33,7 @@ type EntryActionsProps = { onCopyEntry?: () => void; onShowRelatedClick?: () => void; onCopyId?: () => void; + onUpdateSharedEntryBindings?: () => void; }; export const EntryActions = ({ @@ -43,6 +45,7 @@ export const EntryActions = ({ onCopyEntry, onShowRelatedClick, onCopyId, + onUpdateSharedEntryBindings, }: EntryActionsProps) => { const {useAdditionalWorkbookEntryActions} = registry.workbooks.functions.getAll(); @@ -101,6 +104,18 @@ export const EntryActions = ({ items.push(subMenu); } + if (onUpdateSharedEntryBindings) { + items.push({ + action: onUpdateSharedEntryBindings, + text: ( + + ), + }); + } + const otherActions: DropdownMenuItemMixed[] = []; if (onDeleteClick) { diff --git a/src/ui/units/workbooks/components/Table/WorkbookEntriesTable/ChunkGroup/ChunkGroup.tsx b/src/ui/units/workbooks/components/Table/WorkbookEntriesTable/ChunkGroup/ChunkGroup.tsx index 21ea9c8311..0770357408 100644 --- a/src/ui/units/workbooks/components/Table/WorkbookEntriesTable/ChunkGroup/ChunkGroup.tsx +++ b/src/ui/units/workbooks/components/Table/WorkbookEntriesTable/ChunkGroup/ChunkGroup.tsx @@ -3,7 +3,7 @@ import React from 'react'; import block from 'bem-cn-lite'; import {useInView} from 'react-intersection-observer'; import {WorkbookPageQa} from 'shared/constants'; -import type {ChunkItem} from 'ui/units/workbooks/types'; +import type {ChunkItem, WorkbookEntry} from 'ui/units/workbooks/types'; import {EmptyRow, Row} from '../Row/Row'; import {ROW_HEIGHT, options} from '../constants'; @@ -11,13 +11,13 @@ import type {WorkbookEntriesTableProps} from '../types'; import './ChunkGroup.scss'; -interface ChunkGroupProps extends WorkbookEntriesTableProps { - chunk: ChunkItem[]; +interface ChunkGroupProps extends WorkbookEntriesTableProps { + chunk: ChunkItem[]; } const b = block('dl-workbook-entries-chunk-group'); -export function ChunkGroup({ +export function ChunkGroup({ chunk, workbook, onRenameEntry, @@ -26,7 +26,8 @@ export function ChunkGroup({ onCopyEntry, onShowRelatedClick, onCopyId, -}: ChunkGroupProps) { + onUpdateSharedEntryBindings, +}: ChunkGroupProps) { const {ref, inView} = useInView(options); const height = chunk.length * ROW_HEIGHT; @@ -50,6 +51,7 @@ export function ChunkGroup({ onDuplicateEntry={onDuplicateEntry} onCopyEntry={onCopyEntry} onShowRelatedClick={onShowRelatedClick} + onUpdateSharedEntryBindings={onUpdateSharedEntryBindings} onCopyId={onCopyId} /> ); diff --git a/src/ui/units/workbooks/components/Table/WorkbookEntriesTable/MainTabContent/MainTabContent.tsx b/src/ui/units/workbooks/components/Table/WorkbookEntriesTable/MainTabContent/MainTabContent.tsx index cc15f3b24a..f6b762dad4 100644 --- a/src/ui/units/workbooks/components/Table/WorkbookEntriesTable/MainTabContent/MainTabContent.tsx +++ b/src/ui/units/workbooks/components/Table/WorkbookEntriesTable/MainTabContent/MainTabContent.tsx @@ -9,7 +9,7 @@ import {useDispatch} from 'react-redux'; import {DL} from 'ui/constants/common'; import type {CreateEntryActionType} from 'ui/units/workbooks/constants'; import {setCreateWorkbookEntryType} from 'ui/units/workbooks/store/actions'; -import type {ChunkItem} from 'ui/units/workbooks/types'; +import type {ChunkItem, WorkbookEntry} from 'ui/units/workbooks/types'; import {MOBILE_SIZE} from 'ui/utils/mobile'; import {ChunkGroup} from '../ChunkGroup/ChunkGroup'; @@ -22,9 +22,9 @@ const b = block('dl-main-tab-content'); const i18n = I18n.keyset('new-workbooks'); -type MainTabContentProps = WorkbookEntriesTableProps & { +type MainTabContentProps = WorkbookEntriesTableProps & { actionCreateText?: string; - chunk: ChunkItem[]; + chunk: ChunkItem[]; title: string; isErrorMessage?: boolean; isLoading?: boolean; @@ -37,7 +37,7 @@ type MainTabContentProps = WorkbookEntriesTableProps & { hasCreateButton?: boolean; }; -const MainTabContent = ({ +const MainTabContent = ({ workbook, chunk, onRenameEntry, @@ -57,7 +57,8 @@ const MainTabContent = ({ clearView, onShowRelatedClick, onCopyId, -}: MainTabContentProps) => { + onUpdateSharedEntryBindings, +}: MainTabContentProps) => { const [isOpen, setIsOpen] = React.useState(true); const dispatch = useDispatch(); @@ -85,6 +86,7 @@ const MainTabContent = ({ onCopyEntry={onCopyEntry} onShowRelatedClick={onShowRelatedClick} onCopyId={onCopyId} + onUpdateSharedEntryBindings={onUpdateSharedEntryBindings} /> ); } diff --git a/src/ui/units/workbooks/components/Table/WorkbookEntriesTable/Row/Row.tsx b/src/ui/units/workbooks/components/Table/WorkbookEntriesTable/Row/Row.tsx index ed34f41f1e..77c7a18fb1 100644 --- a/src/ui/units/workbooks/components/Table/WorkbookEntriesTable/Row/Row.tsx +++ b/src/ui/units/workbooks/components/Table/WorkbookEntriesTable/Row/Row.tsx @@ -22,16 +22,17 @@ import './Row.scss'; const i18n = I18n.keyset('new-workbooks'); -type RowProps = { - item: WorkbookEntry; +type RowProps = { + item: T; workbook: WorkbookWithPermissions; isOpen?: boolean; - onRenameEntry?: (data: WorkbookEntry) => void; - onDeleteEntry?: (data: WorkbookEntry) => void; - onDuplicateEntry?: (data: WorkbookEntry) => void; - onCopyEntry?: (data: WorkbookEntry) => void; - onShowRelatedClick?: (data: WorkbookEntry) => void; - onCopyId?: (data: WorkbookEntry) => void; + onRenameEntry?: (data: T) => void; + onDeleteEntry?: (data: T) => void; + onDuplicateEntry?: (data: T) => void; + onCopyEntry?: (data: T) => void; + onShowRelatedClick?: (data: T) => void; + onCopyId?: (data: T) => void; + onUpdateSharedEntryBindings?: (data: T) => void; }; const onClickStopPropogation: React.MouseEventHandler = (e) => { @@ -41,7 +42,7 @@ const onClickStopPropogation: React.MouseEventHandler = (e) => { const b = block('dl-content-row'); -const Row: React.FC = ({ +const Row = ({ item, workbook, onRenameEntry, @@ -50,10 +51,10 @@ const Row: React.FC = ({ onCopyEntry, onShowRelatedClick, onCopyId, -}) => { + onUpdateSharedEntryBindings, +}: RowProps) => { const {getWorkbookEntryUrl} = registry.workbooks.functions.getAll(); const {getLoginById} = registry.common.functions.getAll(); - const dispatch: AppDispatch = useDispatch(); const url = getWorkbookEntryUrl(item, workbook); @@ -156,6 +157,10 @@ const Row: React.FC = ({ onShowRelatedClick && (() => onShowRelatedClick(item)) } onCopyId={onCopyId && (() => onCopyId(item))} + onUpdateSharedEntryBindings={ + onUpdateSharedEntryBindings && + (() => onUpdateSharedEntryBindings(item)) + } /> )} diff --git a/src/ui/units/workbooks/components/Table/WorkbookEntriesTable/WorkbookEntriesTable.tsx b/src/ui/units/workbooks/components/Table/WorkbookEntriesTable/WorkbookEntriesTable.tsx index a48a0cda6a..9a7336b0bb 100644 --- a/src/ui/units/workbooks/components/Table/WorkbookEntriesTable/WorkbookEntriesTable.tsx +++ b/src/ui/units/workbooks/components/Table/WorkbookEntriesTable/WorkbookEntriesTable.tsx @@ -6,8 +6,11 @@ import {useDispatch} from 'react-redux'; import type {EntryScope} from 'shared'; import {getUserId} from 'shared/modules/user'; import {DIALOG_COPY_ENTRIES_TO_WORKBOOK} from 'ui/components/CopyEntriesToWorkbookDialog'; +import {DIALOG_SHARED_ENTRY_PERMISSIONS} from 'ui/components/DialogSharedEntryPermissions/DialogSharedEntryPermissions'; import {EntryDialogName, EntryDialogues} from 'ui/components/EntryDialogues'; import {DL} from 'ui/constants/common'; +import {getSdk} from 'ui/libs/schematic-sdk'; +import {showToast} from 'ui/store/actions/toaster'; import {getResolveUsersByIdsAction} from 'ui/store/actions/usersByIds'; import {copyTextWithToast} from 'ui/utils/copyText'; @@ -16,8 +19,9 @@ import type {WorkbookWithPermissions} from '../../../../../../shared/schema/us/t import {registry} from '../../../../../registry'; import type {AppDispatch} from '../../../../../store'; import {closeDialog, openDialog} from '../../../../../store/actions/dialog'; -import type {ChunkItem, WorkbookEntry} from '../../../types'; +import type {ChunkItem, WorkbookEntry, WorkbookSharedEntry} from '../../../types'; import {DIALOG_DELETE_ENTRY_IN_NEW_WORKBOOK} from '../../DeleteEntryDialog/DeleteEntryDialog'; +import {DIALOG_DELETE_SHARED_ENTRY_IN_WORKBOOK} from '../../DeleteSharedEntryDialog/DeleteSharedEntryDialog'; import {DIALOG_DUPLICATE_ENTRY_IN_WORKBOOK} from '../../DuplicateEntryDialog/DuplicateEntryDialog'; import {DIALOG_RENAME_ENTRY_IN_NEW_WORKBOOK} from '../../RenameEntryDialog/RenameEntryDialog'; @@ -35,19 +39,20 @@ type WorkbookEntriesTableProps = { workbook: WorkbookWithPermissions; entries: GetEntryResponse[]; refreshEntries: (scope: EntryScope) => void; + refreshSharedEntries: (scope?: EntryScope) => void; loadMoreEntries?: (entryScope: EntryScope) => void; loadMoreSharedEntries?: (entryScope?: EntryScope) => void; retryLoadSharedEntries?: (entryScope?: EntryScope) => void; sharedToken?: string; sharedError?: boolean; sharedLoader?: boolean; - sharedChunks: ChunkItem[][]; + sharedChunks: ChunkItem[][]; retryLoadEntries?: (entryScope: EntryScope) => void; scope?: EntryScope; mapTokens?: Record; mapErrors?: Record; mapLoaders?: Record; - chunks: ChunkItem[][]; + chunks: ChunkItem[][]; availableScopes?: EntryScope[]; }; @@ -70,6 +75,7 @@ export const WorkbookEntriesTable = React.memo( sharedError, sharedLoader, sharedToken, + refreshSharedEntries, }) => { const dispatch: AppDispatch = useDispatch(); const entryDialoguesRef = React.useRef(null); @@ -141,6 +147,62 @@ export const WorkbookEntriesTable = React.memo( [dispatch, onApplyDuplicate], ); + const onDeleteSharedEntry = React.useCallback( + (entity: WorkbookSharedEntry) => { + dispatch( + openDialog({ + id: DIALOG_DELETE_SHARED_ENTRY_IN_WORKBOOK, + props: { + open: true, + onClose: () => dispatch(closeDialog()), + entry: entity, + workbookId: workbook.workbookId, + onDeleteSuccess: () => { + dispatch(closeDialog()); + refreshSharedEntries(scope); + }, + }, + }), + ); + }, + [dispatch, workbook, refreshSharedEntries, scope], + ); + + const onUpdateSharedEntryBindings = React.useCallback( + (entity: WorkbookSharedEntry) => { + dispatch( + openDialog({ + id: DIALOG_SHARED_ENTRY_PERMISSIONS, + props: { + open: true, + onClose: () => dispatch(closeDialog()), + entry: entity, + delegation: entity.isDelegated, + onApply: async (delegation) => { + try { + await getSdk().sdk.us.updateSharedEntryBinding({ + sourceId: entity.entryId, + targetId: workbook.workbookId, + delegation, + }); + dispatch(closeDialog()); + refreshSharedEntries(scope); + } catch (error) { + dispatch( + showToast({ + title: error.message, + error, + }), + ); + } + }, + }, + }), + ); + }, + [dispatch, workbook, refreshSharedEntries, scope], + ); + const onCopyEntry = React.useCallback( (entity: WorkbookEntry) => { dispatch( @@ -216,6 +278,8 @@ export const WorkbookEntriesTable = React.memo( sharedChunks={sharedChunks} loadMoreSharedEntries={() => loadMoreSharedEntries?.(scope)} retryLoadSharedEntries={() => retryLoadSharedEntries?.(scope)} + onDeleteSharedEntry={onDeleteSharedEntry} + onUpdateSharedEntryBindings={onUpdateSharedEntryBindings} sharedError={sharedError} sharedLoader={sharedLoader} sharedToken={sharedToken} @@ -240,6 +304,8 @@ export const WorkbookEntriesTable = React.memo( availableScopes={availableScopes} onRenameEntry={onRenameEntry} onDeleteEntry={onDeleteEntry} + onDeleteSharedEntry={onDeleteSharedEntry} + onUpdateSharedEntryBindings={onUpdateSharedEntryBindings} onDuplicateEntry={onDuplicateEntry} onCopyEntry={onCopyEntry} onShowRelated={onShowRelated} diff --git a/src/ui/units/workbooks/components/Table/WorkbookEntriesTable/WorkbookEntriesTableTabs.tsx b/src/ui/units/workbooks/components/Table/WorkbookEntriesTable/WorkbookEntriesTableTabs.tsx index d0f45b54b9..e881c1ff12 100644 --- a/src/ui/units/workbooks/components/Table/WorkbookEntriesTable/WorkbookEntriesTableTabs.tsx +++ b/src/ui/units/workbooks/components/Table/WorkbookEntriesTable/WorkbookEntriesTableTabs.tsx @@ -8,7 +8,7 @@ import {DL} from 'ui/constants/common'; import {CreateEntryActionType} from 'ui/units/workbooks/constants'; import type {WorkbookWithPermissions} from '../../../../../../shared/schema/us/types'; -import type {ChunkItem, WorkbookEntry} from '../../../types'; +import type {ChunkItem, WorkbookEntry, WorkbookSharedEntry} from '../../../types'; import {CreateEntry} from '../../CreateEntry/CreateEntry'; import {MainTabContent} from './MainTabContent/MainTabContent'; @@ -32,11 +32,13 @@ export type WorkbookEntriesTableTabsProps = { sharedError?: boolean | null; mapLoaders?: Record; sharedLoader?: boolean; - chunks?: ChunkItem[][]; - sharedChunks?: ChunkItem[][]; + chunks?: ChunkItem[][]; + sharedChunks?: ChunkItem[][]; availableScopes?: EntryScope[]; onRenameEntry?: (data: WorkbookEntry) => void; onDeleteEntry?: (data: WorkbookEntry) => void; + onDeleteSharedEntry?: (data: WorkbookSharedEntry) => void; + onUpdateSharedEntryBindings?: (data: WorkbookSharedEntry) => void; onDuplicateEntry?: (data: WorkbookEntry) => void; onCopyEntry?: (data: WorkbookEntry) => void; onShowRelated?: (data: WorkbookEntry) => void; diff --git a/src/ui/units/workbooks/components/Table/WorkbookEntriesTable/types.ts b/src/ui/units/workbooks/components/Table/WorkbookEntriesTable/types.ts index f52307a92f..b2ceb8fb33 100644 --- a/src/ui/units/workbooks/components/Table/WorkbookEntriesTable/types.ts +++ b/src/ui/units/workbooks/components/Table/WorkbookEntriesTable/types.ts @@ -1,14 +1,15 @@ import type {WorkbookWithPermissions} from 'shared/schema/us/types/workbooks'; import type {WorkbookEntry} from 'ui/units/workbooks/types/index'; -interface WorkbookEntriesTableProps { +interface WorkbookEntriesTableProps { workbook: WorkbookWithPermissions; - onRenameEntry?: (data: WorkbookEntry) => void; - onDeleteEntry?: (data: WorkbookEntry) => void; - onDuplicateEntry?: (data: WorkbookEntry) => void; - onCopyEntry?: (data: WorkbookEntry) => void; - onShowRelatedClick?: (data: WorkbookEntry) => void; - onCopyId?: (data: WorkbookEntry) => void; + onRenameEntry?: (data: T) => void; + onDeleteEntry?: (data: T) => void; + onDuplicateEntry?: (data: T) => void; + onCopyEntry?: (data: T) => void; + onShowRelatedClick?: (data: T) => void; + onCopyId?: (data: T) => void; + onUpdateSharedEntryBindings?: (data: T) => void; } export {WorkbookEntriesTableProps}; diff --git a/src/ui/units/workbooks/components/WorkbookMainTabContent/WorkbookMainTabContent.tsx b/src/ui/units/workbooks/components/WorkbookMainTabContent/WorkbookMainTabContent.tsx index 56bcd2d168..05c0d70b02 100644 --- a/src/ui/units/workbooks/components/WorkbookMainTabContent/WorkbookMainTabContent.tsx +++ b/src/ui/units/workbooks/components/WorkbookMainTabContent/WorkbookMainTabContent.tsx @@ -1,7 +1,7 @@ import React from 'react'; import {SmartLoader} from 'components/SmartLoader/SmartLoader'; -import {useDispatch, useSelector} from 'react-redux'; +import {batch, useDispatch, useSelector} from 'react-redux'; import {EntryScope, Feature} from 'shared'; import type {WorkbookWithPermissions} from 'shared/schema'; import {registry} from 'ui/registry'; @@ -25,7 +25,7 @@ import { selectWorkbookSharedEntriesIsLoading, selectWorkbookSharedItems, } from '../../store/selectors'; -import type {WorkbookEntriesFilters} from '../../types'; +import type {WorkbookEntriesFilters, WorkbookSharedEntry} from '../../types'; import {WorkbookEntriesTable} from '../Table/WorkbookEntriesTable/WorkbookEntriesTable'; import {useChunkedEntries as useSharedChunkedEntries} from '../WorkbookTabContent/useChunkedEntries'; import {TAB_ALL} from '../WorkbookTabs/constants'; @@ -72,7 +72,7 @@ export const WorkbookMainTabContent = React.memo(({filters, workbookId, w availableScopes, }); - const sharedChunks = useSharedChunkedEntries({ + const sharedChunks = useSharedChunkedEntries({ entries: sharedEntries, availableScopes: [EntryScope.Connection, EntryScope.Dataset], }); @@ -80,8 +80,10 @@ export const WorkbookMainTabContent = React.memo(({filters, workbookId, w React.useEffect(() => { if (workbook?.workbookId === workbookId) { (async () => { - dispatch(resetWorkbookEntries()); - dispatch(resetWorkbookSharedEntries()); + batch(() => { + dispatch(resetWorkbookEntries()); + dispatch(resetWorkbookSharedEntries()); + }); setIsLoading(true); @@ -228,33 +230,49 @@ export const WorkbookMainTabContent = React.memo(({filters, workbookId, w setMapLoaders({ [entryScope]: true, }); - dispatch(resetWorkbookEntriesByScope(entryScope)); + batch(() => { + dispatch(resetWorkbookEntriesByScope(entryScope)); - dispatch( - getWorkbookEntries({ - workbookId, - filters, - scope: entryScope, - pageSize: PAGE_SIZE_MAIN_TAB, - }), - ) - .then((data) => { - if (data) { - setMapTokens({ - ...mapTokens, - [entryScope]: data?.nextPageToken || '', + dispatch( + getWorkbookEntries({ + workbookId, + filters, + scope: entryScope, + pageSize: PAGE_SIZE_MAIN_TAB, + }), + ) + .then((data) => { + if (data) { + setMapTokens({ + ...mapTokens, + [entryScope]: data?.nextPageToken || '', + }); + } + }) + .finally(() => { + setMapLoaders({ + [entryScope]: false, }); - } - }) - .finally(() => { - setMapLoaders({ - [entryScope]: false, }); - }); + }); }, [dispatch, workbookId, filters, mapTokens], ); + const refreshSharedEntries = React.useCallback(() => { + batch(() => { + dispatch(resetWorkbookSharedEntries()); + + dispatch( + getWorkbookSharedEntries({ + workbookId, + filters, + pageSize: PAGE_SIZE_MAIN_TAB, + }), + ); + }); + }, [dispatch, workbookId, filters]); + if ( (isEntriesLoading || isLoading) && entries.length === 0 && @@ -267,6 +285,7 @@ export const WorkbookMainTabContent = React.memo(({filters, workbookId, w { +}): ChunkItem[][] => { const chunks = React.useMemo(() => { const allowedScopes = new Set(availableScopes); @@ -29,14 +29,16 @@ export const useChunkedEntries = ({ if (workbookEntries.length === 0) { return []; } else { - const chunkArrays = availableScopes.map(() => [] as Array); + const chunkArrays = availableScopes.map( + () => [] as Array>, + ); workbookEntries.forEach((chunkItem) => { const item = { type: 'entry', item: chunkItem, key: chunkItem.entryId, - } as EntryChunkItem; + } as EntryChunkItem; const chunkIndex = availableScopes.findIndex((scope) => scope === chunkItem.scope); diff --git a/src/ui/units/workbooks/components/WorkbookTabContent/WorkbookTabContent.tsx b/src/ui/units/workbooks/components/WorkbookTabContent/WorkbookTabContent.tsx index 3d0629ab6c..8bf0468ffb 100644 --- a/src/ui/units/workbooks/components/WorkbookTabContent/WorkbookTabContent.tsx +++ b/src/ui/units/workbooks/components/WorkbookTabContent/WorkbookTabContent.tsx @@ -4,7 +4,7 @@ import {Button} from '@gravity-ui/uikit'; import block from 'bem-cn-lite'; import {SmartLoader} from 'components/SmartLoader/SmartLoader'; import {I18n} from 'i18n'; -import {useDispatch, useSelector} from 'react-redux'; +import {batch, useDispatch, useSelector} from 'react-redux'; import {Waypoint} from 'react-waypoint'; import {EntryScope, Feature} from 'shared'; import type {WorkbookWithPermissions} from 'shared/schema'; @@ -29,7 +29,7 @@ import { selectWorkbookSharedEntriesIsLoading, selectWorkbookSharedItems, } from '../../store/selectors'; -import type {WorkbookEntriesFilters} from '../../types'; +import type {WorkbookEntriesFilters, WorkbookSharedEntry} from '../../types'; import {EmptyWorkbookContainer} from '../EmptyWorkbook/EmptyWorkbookContainer'; import {WorkbookEntriesTable} from '../Table/WorkbookEntriesTable/WorkbookEntriesTable'; import {TAB_ALL} from '../WorkbookTabs/constants'; @@ -75,7 +75,7 @@ export const WorkbookTabContent = React.memo(({workbookId, workbook, filt }, [getWorkbookTabs, workbook]) as EntryScope[]; const chunks = useChunkedEntries({entries, availableScopes}); - const sharedChunks = useChunkedEntries({ + const sharedChunks = useChunkedEntries({ entries: sharedEntries, availableScopes: [EntryScope.Connection, EntryScope.Dataset], }); @@ -83,20 +83,35 @@ export const WorkbookTabContent = React.memo(({workbookId, workbook, filt const dispatch = useDispatch(); React.useEffect(() => { - dispatch(resetWorkbookEntries()); - dispatch(resetWorkbookSharedEntries()); + batch(() => { + dispatch(resetWorkbookEntries()); + dispatch(resetWorkbookSharedEntries()); - dispatch(getWorkbookEntries({workbookId, filters, scope})); - if (isSharedEntriesEnabled) { - dispatch(getWorkbookSharedEntries({workbookId, filters, scope})); - } + dispatch(getWorkbookEntries({workbookId, filters, scope})); + if (isSharedEntriesEnabled) { + dispatch(getWorkbookSharedEntries({workbookId, filters, scope})); + } + }); }, [dispatch, filters, scope, workbook, workbookId, isSharedEntriesEnabled]); const refreshEntries = React.useCallback( (entryScope: EntryScope) => { - dispatch(resetWorkbookEntries()); + batch(() => { + dispatch(resetWorkbookEntries()); + + dispatch(getWorkbookEntries({workbookId, filters, scope: entryScope})); + }); + }, + [dispatch, workbookId, filters], + ); + + const refreshSharedEntries = React.useCallback( + (entryScope?: EntryScope) => { + batch(() => { + dispatch(resetWorkbookSharedEntries()); - dispatch(getWorkbookEntries({workbookId, filters, scope: entryScope})); + dispatch(getWorkbookSharedEntries({workbookId, filters, scope: entryScope})); + }); }, [dispatch, workbookId, filters], ); @@ -194,6 +209,7 @@ export const WorkbookTabContent = React.memo(({workbookId, workbook, filt return ( ({ entries, availableScopes, }: { entries: GetEntryResponse[]; availableScopes: EntryScope[]; -}): ChunkItem[][] => { +}): ChunkItem[][] => { const chunks = React.useMemo(() => { const allowedScopes = new Set(availableScopes); const workbookEntries = entries .filter((item) => allowedScopes.has(item.scope as EntryScope)) .map((item) => { - const workbookEntry: WorkbookEntry = { + const workbookEntry = { name: Utils.getEntryNameFromKey(item.key), ...item, - }; + } as T; return workbookEntry; }); - const items: ChunkItem[] = []; + const items: ChunkItem[] = []; if (workbookEntries.length === 0) { items.push({ @@ -37,7 +37,7 @@ export const useChunkedEntries = ({ }); } else { items.push( - ...workbookEntries.map((item) => ({ + ...workbookEntries.map>((item) => ({ type: 'entry', item, key: item.entryId, diff --git a/src/ui/units/workbooks/store/actions/index.ts b/src/ui/units/workbooks/store/actions/index.ts index 2547fdb21e..33498141b9 100644 --- a/src/ui/units/workbooks/store/actions/index.ts +++ b/src/ui/units/workbooks/store/actions/index.ts @@ -305,6 +305,7 @@ export const getWorkbookSharedEntries = ({ const args: GetWorkbookEntriesArgs = { workbookId, pageSize, + includePermissionsInfo: true, page: Number(nextPageToken || 0), orderBy: { field: filters.orderField, diff --git a/src/ui/units/workbooks/store/reducers/workbook-page.ts b/src/ui/units/workbooks/store/reducers/workbook-page.ts index c316e6fb80..ee695063e1 100644 --- a/src/ui/units/workbooks/store/reducers/workbook-page.ts +++ b/src/ui/units/workbooks/store/reducers/workbook-page.ts @@ -6,6 +6,7 @@ import type { GetCollectionBreadcrumbsResponse, GetEntryResponse, GetWorkbookEntriesResponse, + GetWorkbookSharedEntriesResponse, RenameEntryResponse, WorkbookPermission, WorkbookWithPermissions, @@ -70,7 +71,7 @@ export type WorkbooksState = { isLoading: boolean; error: Error | null; }; - sharedItems: GetWorkbookEntriesResponse['entries']; + sharedItems: GetWorkbookSharedEntriesResponse['entries']; items: GetWorkbookEntriesResponse['entries']; getWorkbook: { isLoading: boolean; diff --git a/src/ui/units/workbooks/types/index.ts b/src/ui/units/workbooks/types/index.ts index 5cef9bdbf8..54340b6ba0 100644 --- a/src/ui/units/workbooks/types/index.ts +++ b/src/ui/units/workbooks/types/index.ts @@ -1,3 +1,5 @@ +import type {SharedScope} from 'shared'; + import type {GetEntryResponse} from '../../../../shared/schema'; import type { OrderDirection, @@ -11,10 +13,11 @@ export type WorkbookEntriesFilters = { }; export type WorkbookEntry = GetEntryResponse & {name: string}; +export type WorkbookSharedEntry = WorkbookEntry & {isDelegated: boolean; scope: SharedScope}; -export type EntryChunkItem = { +export type EntryChunkItem = { type: 'entry'; - item: WorkbookEntry; + item: T; key: string; }; @@ -23,4 +26,4 @@ export type EmptyChunkItem = { key: 'empty'; }; -export type ChunkItem = EntryChunkItem | EmptyChunkItem; +export type ChunkItem = EntryChunkItem | EmptyChunkItem;