From d1fe4693123e36e31eb1b7bff0e8a4d439b8eda7 Mon Sep 17 00:00:00 2001 From: tygao Date: Fri, 27 Oct 2023 17:07:41 +0800 Subject: [PATCH] update workspace list page (#238) * feat: update workspace list Signed-off-by: tygao * test: remove failed snapshots temporarily Signed-off-by: tygao * add i18n Signed-off-by: tygao * update create workspace url Signed-off-by: tygao * use cleanWorkspaceId Signed-off-by: tygao * update menu link Signed-off-by: tygao --------- Signed-off-by: tygao --- src/core/public/index.ts | 1 + .../public/components/utils/common.ts | 17 ++ .../public/components/utils/workspace.ts | 15 +- .../components/workspace_list/index.tsx | 158 +++++++++++++----- .../workspace_menu/workspace_menu.tsx | 13 +- 5 files changed, 156 insertions(+), 48 deletions(-) create mode 100644 src/plugins/workspace/public/components/utils/common.ts diff --git a/src/core/public/index.ts b/src/core/public/index.ts index 08000926d7aa..484ff309ef4d 100644 --- a/src/core/public/index.ts +++ b/src/core/public/index.ts @@ -357,4 +357,5 @@ export { PUBLIC_WORKSPACE_ID, MANAGEMENT_WORKSPACE_ID, WORKSPACE_TYPE, + cleanWorkspaceId, } from '../utils'; diff --git a/src/plugins/workspace/public/components/utils/common.ts b/src/plugins/workspace/public/components/utils/common.ts new file mode 100644 index 000000000000..cadd938e36d9 --- /dev/null +++ b/src/plugins/workspace/public/components/utils/common.ts @@ -0,0 +1,17 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export const debounce = (func: Function, delay: number) => { + let timerId: NodeJS.Timeout; + + return (...args: any) => { + if (!timerId) { + func(...args); + } + clearTimeout(timerId); + + timerId = setTimeout(() => func(...args), delay); + }; +}; diff --git a/src/plugins/workspace/public/components/utils/workspace.ts b/src/plugins/workspace/public/components/utils/workspace.ts index 6be21538838f..a0b49c520b01 100644 --- a/src/plugins/workspace/public/components/utils/workspace.ts +++ b/src/plugins/workspace/public/components/utils/workspace.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { WORKSPACE_OVERVIEW_APP_ID } from '../../../common/constants'; +import { WORKSPACE_OVERVIEW_APP_ID, WORKSPACE_UPDATE_APP_ID } from '../../../common/constants'; import { CoreStart } from '../../../../../core/public'; import { formatUrlWithWorkspaceId } from '../../../../../core/public/utils'; @@ -21,3 +21,16 @@ export const switchWorkspace = ({ application, http }: Core, id: string) => { window.location.href = newUrl; } }; + +export const updateWorkspace = ({ application, http }: Core, id: string) => { + const newUrl = formatUrlWithWorkspaceId( + application.getUrlForApp(WORKSPACE_UPDATE_APP_ID, { + absolute: true, + }), + id, + http.basePath + ); + if (newUrl) { + window.location.href = newUrl; + } +}; diff --git a/src/plugins/workspace/public/components/workspace_list/index.tsx b/src/plugins/workspace/public/components/workspace_list/index.tsx index 5599ae6bcf77..201f1928b8fc 100644 --- a/src/plugins/workspace/public/components/workspace_list/index.tsx +++ b/src/plugins/workspace/public/components/workspace_list/index.tsx @@ -3,45 +3,53 @@ * SPDX-License-Identifier: Apache-2.0 */ -import React, { useState } from 'react'; +import React, { useState, useMemo, useCallback } from 'react'; import { EuiPage, EuiPageBody, EuiPageHeader, EuiPageContent, - EuiBasicTable, EuiLink, - Direction, - CriteriaWithPagination, + EuiButton, + EuiInMemoryTable, + EuiTableSelectionType, + EuiSearchBarProps, } from '@elastic/eui'; import useObservable from 'react-use/lib/useObservable'; -import { useMemo, useCallback } from 'react'; import { of } from 'rxjs'; +import { i18n } from '@osd/i18n'; import { WorkspaceAttribute } from '../../../../../core/public'; import { useOpenSearchDashboards } from '../../../../../plugins/opensearch_dashboards_react/public'; -import { switchWorkspace } from '../utils/workspace'; +import { switchWorkspace, updateWorkspace } from '../utils/workspace'; +import { debounce } from '../utils/common'; + +import { WORKSPACE_CREATE_APP_ID } from '../../../common/constants'; + +import { cleanWorkspaceId } from '../../../../../core/public'; + +const WORKSPACE_LIST_PAGE_DESCRIPTIOIN = i18n.translate('workspace.list.description', { + defaultMessage: + 'Workspace allow you to save and organize library items, such as index patterns, visualizations, dashboards, saved searches, and share them with other OpenSearch Dashboards users. You can control which features are visible in each workspace, and which users and groups have read and write access to the library items in the workspace.', +}); export const WorkspaceList = () => { const { services: { workspaces, application, http }, } = useOpenSearchDashboards(); - const [pageIndex, setPageIndex] = useState(0); - const [pageSize, setPageSize] = useState(5); - const [sortField, setSortField] = useState<'name' | 'id'>('name'); - const [sortDirection, setSortDirection] = useState('asc'); - + const initialSortField = 'name'; + const initialSortDirection = 'asc'; const workspaceList = useObservable(workspaces?.workspaceList$ ?? of([]), []); + const [queryInput, setQueryInput] = useState(''); + const [pagination, setPagination] = useState({ + pageIndex: 0, + pageSize: 5, + pageSizeOptions: [5, 10, 20], + }); - const pageOfItems = useMemo(() => { - return workspaceList - .sort((a, b) => { - const compare = a[sortField].localeCompare(b[sortField]); - return sortDirection === 'asc' ? compare : -compare; - }) - .slice(pageIndex * pageSize, (pageIndex + 1) * pageSize); - }, [workspaceList, pageIndex, pageSize, sortField, sortDirection]); + // Will be uesed when updating table actions + const [, setSelection] = useState([]); const handleSwitchWorkspace = useCallback( (id: string) => { @@ -52,6 +60,29 @@ export const WorkspaceList = () => { [application, http] ); + const handleUpdateWorkspace = useCallback( + (id: string) => { + if (application && http) { + updateWorkspace({ application, http }, id); + } + }, + [application, http] + ); + + const searchResult = useMemo(() => { + if (queryInput) { + const normalizedQuery = queryInput.toLowerCase(); + const result = workspaceList.filter((item) => { + return ( + item.id.toLowerCase().indexOf(normalizedQuery) > -1 || + item.name.toLowerCase().indexOf(normalizedQuery) > -1 + ); + }); + return result; + } + return workspaceList; + }, [workspaceList, queryInput]); + const columns = [ { field: 'name', @@ -79,46 +110,95 @@ export const WorkspaceList = () => { isExpander: true, hasActions: true, }, + { + name: 'Actions', + field: '', + actions: [ + { + name: 'Edit', + icon: 'pencil', + type: 'icon', + description: 'edit workspace', + onClick: ({ id }: WorkspaceAttribute) => handleUpdateWorkspace(id), + }, + ], + }, ]; - const onTableChange = ({ page, sort }: CriteriaWithPagination) => { - const { field, direction } = sort!; - const { index, size } = page; + const workspaceCreateUrl = useMemo(() => { + if (!application || !http) { + return ''; + } + + return cleanWorkspaceId( + application.getUrlForApp(WORKSPACE_CREATE_APP_ID, { + absolute: false, + }) + ); + }, [application, http]); + + const debouncedSetQueryInput = useMemo(() => { + return debounce(setQueryInput, 300); + }, [setQueryInput]); + + const handleSearchInput: EuiSearchBarProps['onChange'] = ({ query }) => { + debouncedSetQueryInput(query?.text ?? ''); + }; + + const search: EuiSearchBarProps = { + onChange: handleSearchInput, + box: { + incremental: true, + }, + toolsRight: [ + + Create workspace + , + ], + }; - setPageIndex(index); - setPageSize(size); - setSortField(field as 'name' | 'id'); - setSortDirection(direction); + const selectionValue: EuiTableSelectionType = { + selectable: () => true, + onSelectionChange: (selection) => { + setSelection(selection); + }, }; return ( - + - + setPagination((prev) => { + return { ...prev, pageIndex: index, pageSize: size }; + }) + } + pagination={pagination} sorting={{ sort: { - field: sortField, - direction: sortDirection, + field: initialSortField, + direction: initialSortDirection, }, }} - onChange={onTableChange} + isSelectable={true} + selection={selectionValue} + search={search} /> diff --git a/src/plugins/workspace/public/components/workspace_menu/workspace_menu.tsx b/src/plugins/workspace/public/components/workspace_menu/workspace_menu.tsx index 4c0868a8b373..05ada930abc4 100644 --- a/src/plugins/workspace/public/components/workspace_menu/workspace_menu.tsx +++ b/src/plugins/workspace/public/components/workspace_menu/workspace_menu.tsx @@ -30,6 +30,7 @@ import { WORKSPACE_OVERVIEW_APP_ID, } from '../../../common/constants'; import { formatUrlWithWorkspaceId } from '../../../../../core/public/utils'; +import { cleanWorkspaceId } from '../../../../../core/public'; interface Props { getUrlForApp: ApplicationStart['getUrlForApp']; @@ -117,12 +118,10 @@ export const WorkspaceMenu = ({ basePath, getUrlForApp, workspaces, navigateToUr key: length.toString(), onClick: () => { navigateToUrl( - formatUrlWithWorkspaceId( + cleanWorkspaceId( getUrlForApp(WORKSPACE_CREATE_APP_ID, { absolute: false, - }), - currentWorkspace?.id ?? '', - basePath + }) ) ); setPopover(false); @@ -136,12 +135,10 @@ export const WorkspaceMenu = ({ basePath, getUrlForApp, workspaces, navigateToUr key: (length + 1).toString(), onClick: () => { navigateToUrl( - formatUrlWithWorkspaceId( + cleanWorkspaceId( getUrlForApp(WORKSPACE_LIST_APP_ID, { absolute: false, - }), - currentWorkspace?.id ?? '', - basePath + }) ) ); setPopover(false);