Skip to content

Commit

Permalink
feat: duplicate all and single objects (opensearch-project#121) (open…
Browse files Browse the repository at this point in the history
…search-project#147)

* implement all duplicate copy modal



* add spacer after checkbox list



* add fail message for copy saved objects



* change title wording to manage library



* single duplicate



* change wording



* remove comment



* bug fix: keep selected saved objects info when cancel duplicate all



* fix typo



* use icu syntax in copy message



* bug fix: keep selected saved objects info when cancel duplicate single



* set current workspace as the first option



* update snapshot



* resolve conflict



* update snapshot



* bug fix for saved object table



* update snapshot



* remove unused file



* change i18n constant



* remove empty push



* hide duplicate when workspace is disabled



* update snapshots



---------


(cherry picked from commit 6231768)

Signed-off-by: yuye-aws <yuyezhu@amazon.com>
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
  • Loading branch information
1 parent 1794017 commit b33221a
Show file tree
Hide file tree
Showing 9 changed files with 226 additions and 64 deletions.
6 changes: 3 additions & 3 deletions src/plugins/saved_objects_management/public/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@

import { i18n } from '@osd/i18n';

export const ALL_LIBRARY_OBJECTS_WORDINGS = i18n.translate(
'savedObjectsManagement.allLibraryObjects',
export const MANAGE_LIBRARY_TITLE_WORDINGS = i18n.translate(
'savedObjectsManagement.manageLibrary',
{
defaultMessage: 'All library objects',
defaultMessage: 'Manage library',
}
);

Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

import React from 'react';
import { FormattedMessage } from '@osd/i18n/react';

import { groupBy } from 'lodash';
import {
EuiButton,
EuiButtonEmpty,
Expand All @@ -36,6 +36,7 @@ import { i18n } from '@osd/i18n';
import { SavedObjectWithMetadata } from '../../../types';
import { getSavedObjectLabel } from '../../../lib';
import { SAVED_OBJECT_TYPE_WORKSAPCE } from '../../../constants';
import { CopyState } from '../';

type WorkspaceOption = EuiComboBoxOptionOption<WorkspaceAttribute>;

Expand All @@ -47,6 +48,7 @@ interface Props {
targetWorkspace: string
) => Promise<void>;
onClose: () => void;
copyState: CopyState;
getCopyWorkspaces: () => Promise<WorkspaceAttribute[]>;
selectedSavedObjects: SavedObjectWithMetadata[];
}
Expand All @@ -58,6 +60,11 @@ interface State {
targetWorkspaceOption: WorkspaceOption[];
isLoading: boolean;
isIncludeReferencesDeepChecked: boolean;
savedObjectTypeInfoMap: Map<string, [number, boolean]>;
}

function capitalizeFirstLetter(str: string): string {
return str.charAt(0).toUpperCase() + str.slice(1);
}

export class SavedObjectsCopyModal extends React.Component<Props, State> {
Expand All @@ -73,33 +80,56 @@ export class SavedObjectsCopyModal extends React.Component<Props, State> {
targetWorkspaceOption: [],
isLoading: false,
isIncludeReferencesDeepChecked: true,
savedObjectTypeInfoMap: new Map<string, [number, boolean]>(),
};
}

workspaceToOption = (workspace: WorkspaceAttribute): WorkspaceOption => {
return { label: workspace.name, key: workspace.id, value: workspace };
workspaceToOption = (
workspace: WorkspaceAttribute,
currentWorkspaceName?: string
): WorkspaceOption => {
// add (current) after current workspace name
let workspaceName = workspace.name;
if (workspace.name === currentWorkspaceName) {
workspaceName += ' (current)';
}
return {
label: workspaceName,
key: workspace.id,
value: workspace,
};
};

async componentDidMount() {
const { workspaces, getCopyWorkspaces } = this.props;
const workspaceList = await getCopyWorkspaces();
const currentWorkspace = workspaces.currentWorkspace$;
const currentWorkspace = workspaces.currentWorkspace$.value;
const currentWorkspaceName = currentWorkspace?.name;

if (!!currentWorkspace?.value?.name) {
const currentWorkspaceName = currentWorkspace.value.name;
const filteredWorkspaceOptions = workspaceList
.map(this.workspaceToOption)
.filter((item: WorkspaceOption) => item.label !== currentWorkspaceName);
this.setState({
workspaceOptions: filteredWorkspaceOptions,
allWorkspaceOptions: filteredWorkspaceOptions,
});
} else {
const allWorkspaceOptions = workspaceList.map(this.workspaceToOption);
this.setState({
workspaceOptions: allWorkspaceOptions,
allWorkspaceOptions,
});
// current workspace is the first option
const workspaceOptions = [
...(currentWorkspace ? [this.workspaceToOption(currentWorkspace, currentWorkspaceName)] : []),
...workspaceList
.filter((workspace: WorkspaceAttribute) => workspace.name !== currentWorkspaceName)
.map((workspace: WorkspaceAttribute) =>
this.workspaceToOption(workspace, currentWorkspaceName)
),
];

this.setState({
workspaceOptions,
allWorkspaceOptions: workspaceOptions,
});

const { copyState } = this.props;
if (copyState === CopyState.All) {
const { allSelectedObjects } = this.state;
const categorizedObjects = groupBy(allSelectedObjects, (object) => object.type);
const savedObjectTypeInfoMap = new Map<string, [number, boolean]>();
for (const [savedObjectType, savedObjects] of Object.entries(categorizedObjects)) {
savedObjectTypeInfoMap.set(savedObjectType, [savedObjects.length, true]);
}
this.setState({ savedObjectTypeInfoMap });
}

this.isMounted = true;
Expand Down Expand Up @@ -149,26 +179,93 @@ export class SavedObjectsCopyModal extends React.Component<Props, State> {
}));
};

changeIncludeSavedObjectType = (savedObjectType: string) => {
const { savedObjectTypeInfoMap } = this.state;
const savedObjectTypeInfo = savedObjectTypeInfoMap.get(savedObjectType);
if (savedObjectTypeInfo) {
const [count, checked] = savedObjectTypeInfo;
savedObjectTypeInfoMap.set(savedObjectType, [count, !checked]);
this.setState({ savedObjectTypeInfoMap });
}
};

renderCopyObjectCategory = (
savedObjectType: string,
savedObjectTypeCount: number,
savedObjectTypeChecked: boolean
) => {
return (
<EuiCheckbox
id={'includeSavedObjectType.' + savedObjectType}
key={savedObjectType}
label={
<FormattedMessage
id={'savedObjectsManagement.objectsTable.copyModal.savedObjectType.' + savedObjectType}
defaultMessage={
capitalizeFirstLetter(savedObjectType) + ` (${savedObjectTypeCount.toString()})`
}
/>
}
checked={savedObjectTypeChecked}
onChange={() => this.changeIncludeSavedObjectType(savedObjectType)}
/>
);
};

renderCopyObjectCategories = () => {
const { savedObjectTypeInfoMap } = this.state;
const checkboxList: JSX.Element[] = [];
savedObjectTypeInfoMap.forEach(
([savedObjectTypeCount, savedObjectTypeChecked], savedObjectType) =>
checkboxList.push(
this.renderCopyObjectCategory(
savedObjectType,
savedObjectTypeCount,
savedObjectTypeChecked
)
)
);
return checkboxList;
};

isSavedObjectTypeIncluded = (savedObjectType: string) => {
const { savedObjectTypeInfoMap } = this.state;
const savedObjectTypeInfo = savedObjectTypeInfoMap.get(savedObjectType);
return savedObjectTypeInfo && savedObjectTypeInfo[1];
};

render() {
const {
workspaceOptions,
targetWorkspaceOption,
isIncludeReferencesDeepChecked,
allSelectedObjects,
} = this.state;
const { copyState } = this.props;
const targetWorkspaceId = targetWorkspaceOption?.at(0)?.key;
const includedSelectedObjects = allSelectedObjects.filter((item) =>
let selectedObjects = allSelectedObjects;
if (copyState === CopyState.All) {
selectedObjects = selectedObjects.filter((item) => this.isSavedObjectTypeIncluded(item.type));
}
const includedSelectedObjects = selectedObjects.filter((item) =>
!!targetWorkspaceId && !!item.workspaces
? !item.workspaces.includes(targetWorkspaceId)
: item.type !== SAVED_OBJECT_TYPE_WORKSAPCE
);
const ignoredSelectedObjectsLength = allSelectedObjects.length - includedSelectedObjects.length;

const ignoredSelectedObjectsLength = selectedObjects.length - includedSelectedObjects.length;

let confirmCopyButtonEnabled = false;
if (!!targetWorkspaceId && includedSelectedObjects.length > 0) {
confirmCopyButtonEnabled = true;
}

const confirmMessageForAllObjects = `Duplicate (${includedSelectedObjects.length})`;
const confirmMessageForSingleOrSelectedObjects = 'Duplicate';
const confirmMessage =
copyState === CopyState.All
? confirmMessageForAllObjects
: confirmMessageForSingleOrSelectedObjects;
const warningMessageForOnlyOneSavedObject = (
<p>
<b style={{ color: '#000' }}>1</b> saved object will <b style={{ color: '#000' }}>not</b> be
Expand All @@ -179,7 +276,7 @@ export class SavedObjectsCopyModal extends React.Component<Props, State> {
<p>
<b style={{ color: '#000' }}>{ignoredSelectedObjectsLength}</b> saved objects will{' '}
<b style={{ color: '#000' }}>not</b> be copied, because they have already existed in the
selected workspace or they are worksapces themselves.
selected workspace or they are workspaces themselves.
</p>
);

Expand Down Expand Up @@ -209,11 +306,12 @@ export class SavedObjectsCopyModal extends React.Component<Props, State> {
<EuiModalHeaderTitle>
<FormattedMessage
id="savedObjectsManagement.objectsTable.copyModal.title"
defaultMessage={
'Duplicate ' +
allSelectedObjects.length.toString() +
(allSelectedObjects.length > 1 ? ' objects?' : ' object?')
}
defaultMessage="Duplicate {copyState, select, all {all objects} other {{objectCount, plural, =1 {{objectName}} other {# objects}}}}?"
values={{
copyState,
objectName: allSelectedObjects[0].meta.title,
objectCount: allSelectedObjects.length,
}}
/>
</EuiModalHeaderTitle>
</EuiModalHeader>
Expand Down Expand Up @@ -246,6 +344,8 @@ export class SavedObjectsCopyModal extends React.Component<Props, State> {
</EuiFormRow>

<EuiSpacer size="m" />
{copyState && this.renderCopyObjectCategories()}
{copyState && <EuiSpacer size="m" />}

<EuiFormRow
fullWidth
Expand Down Expand Up @@ -338,7 +438,7 @@ export class SavedObjectsCopyModal extends React.Component<Props, State> {
>
<FormattedMessage
id="savedObjectsManagement.objectsTable.copyModal.confirmButtonLabel"
defaultMessage="Duplicate"
defaultMessage={confirmMessage}
/>
</EuiButton>
</EuiModalFooter>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export const Header = ({
onRefresh,
filteredCount,
title,
selectedCount,
objectCount,
hideImport = false,
showDuplicateAll = false,
}: {
Expand All @@ -57,7 +57,7 @@ export const Header = ({
onRefresh: () => void;
filteredCount: number;
title: string;
selectedCount: number;
objectCount: number;
hideImport: boolean;
showDuplicateAll: boolean;
}) => (
Expand All @@ -77,7 +77,7 @@ export const Header = ({
size="s"
data-test-subj="copyObjects"
onClick={onCopy}
disabled={selectedCount === 0}
disabled={objectCount === 0}
>
<FormattedMessage
id="savedObjectsManagement.objectsTable.header.duplicateAllButtonLabel"
Expand Down
Loading

0 comments on commit b33221a

Please sign in to comment.