Skip to content

Commit d1fe469

Browse files
authored
update workspace list page (opensearch-project#238)
* feat: update workspace list Signed-off-by: tygao <[email protected]> * test: remove failed snapshots temporarily Signed-off-by: tygao <[email protected]> * add i18n Signed-off-by: tygao <[email protected]> * update create workspace url Signed-off-by: tygao <[email protected]> * use cleanWorkspaceId Signed-off-by: tygao <[email protected]> * update menu link Signed-off-by: tygao <[email protected]> --------- Signed-off-by: tygao <[email protected]>
1 parent f4dfa30 commit d1fe469

File tree

5 files changed

+156
-48
lines changed

5 files changed

+156
-48
lines changed

src/core/public/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,4 +357,5 @@ export {
357357
PUBLIC_WORKSPACE_ID,
358358
MANAGEMENT_WORKSPACE_ID,
359359
WORKSPACE_TYPE,
360+
cleanWorkspaceId,
360361
} from '../utils';
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
export const debounce = (func: Function, delay: number) => {
7+
let timerId: NodeJS.Timeout;
8+
9+
return (...args: any) => {
10+
if (!timerId) {
11+
func(...args);
12+
}
13+
clearTimeout(timerId);
14+
15+
timerId = setTimeout(() => func(...args), delay);
16+
};
17+
};

src/plugins/workspace/public/components/utils/workspace.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6-
import { WORKSPACE_OVERVIEW_APP_ID } from '../../../common/constants';
6+
import { WORKSPACE_OVERVIEW_APP_ID, WORKSPACE_UPDATE_APP_ID } from '../../../common/constants';
77
import { CoreStart } from '../../../../../core/public';
88
import { formatUrlWithWorkspaceId } from '../../../../../core/public/utils';
99

@@ -21,3 +21,16 @@ export const switchWorkspace = ({ application, http }: Core, id: string) => {
2121
window.location.href = newUrl;
2222
}
2323
};
24+
25+
export const updateWorkspace = ({ application, http }: Core, id: string) => {
26+
const newUrl = formatUrlWithWorkspaceId(
27+
application.getUrlForApp(WORKSPACE_UPDATE_APP_ID, {
28+
absolute: true,
29+
}),
30+
id,
31+
http.basePath
32+
);
33+
if (newUrl) {
34+
window.location.href = newUrl;
35+
}
36+
};

src/plugins/workspace/public/components/workspace_list/index.tsx

Lines changed: 119 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,45 +3,53 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6-
import React, { useState } from 'react';
6+
import React, { useState, useMemo, useCallback } from 'react';
77
import {
88
EuiPage,
99
EuiPageBody,
1010
EuiPageHeader,
1111
EuiPageContent,
12-
EuiBasicTable,
1312
EuiLink,
14-
Direction,
15-
CriteriaWithPagination,
13+
EuiButton,
14+
EuiInMemoryTable,
15+
EuiTableSelectionType,
16+
EuiSearchBarProps,
1617
} from '@elastic/eui';
1718
import useObservable from 'react-use/lib/useObservable';
18-
import { useMemo, useCallback } from 'react';
1919
import { of } from 'rxjs';
20+
import { i18n } from '@osd/i18n';
2021
import { WorkspaceAttribute } from '../../../../../core/public';
2122

2223
import { useOpenSearchDashboards } from '../../../../../plugins/opensearch_dashboards_react/public';
23-
import { switchWorkspace } from '../utils/workspace';
24+
import { switchWorkspace, updateWorkspace } from '../utils/workspace';
25+
import { debounce } from '../utils/common';
26+
27+
import { WORKSPACE_CREATE_APP_ID } from '../../../common/constants';
28+
29+
import { cleanWorkspaceId } from '../../../../../core/public';
30+
31+
const WORKSPACE_LIST_PAGE_DESCRIPTIOIN = i18n.translate('workspace.list.description', {
32+
defaultMessage:
33+
'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.',
34+
});
2435

2536
export const WorkspaceList = () => {
2637
const {
2738
services: { workspaces, application, http },
2839
} = useOpenSearchDashboards();
2940

30-
const [pageIndex, setPageIndex] = useState(0);
31-
const [pageSize, setPageSize] = useState(5);
32-
const [sortField, setSortField] = useState<'name' | 'id'>('name');
33-
const [sortDirection, setSortDirection] = useState<Direction>('asc');
34-
41+
const initialSortField = 'name';
42+
const initialSortDirection = 'asc';
3543
const workspaceList = useObservable(workspaces?.workspaceList$ ?? of([]), []);
44+
const [queryInput, setQueryInput] = useState<string>('');
45+
const [pagination, setPagination] = useState({
46+
pageIndex: 0,
47+
pageSize: 5,
48+
pageSizeOptions: [5, 10, 20],
49+
});
3650

37-
const pageOfItems = useMemo(() => {
38-
return workspaceList
39-
.sort((a, b) => {
40-
const compare = a[sortField].localeCompare(b[sortField]);
41-
return sortDirection === 'asc' ? compare : -compare;
42-
})
43-
.slice(pageIndex * pageSize, (pageIndex + 1) * pageSize);
44-
}, [workspaceList, pageIndex, pageSize, sortField, sortDirection]);
51+
// Will be uesed when updating table actions
52+
const [, setSelection] = useState<WorkspaceAttribute[]>([]);
4553

4654
const handleSwitchWorkspace = useCallback(
4755
(id: string) => {
@@ -52,6 +60,29 @@ export const WorkspaceList = () => {
5260
[application, http]
5361
);
5462

63+
const handleUpdateWorkspace = useCallback(
64+
(id: string) => {
65+
if (application && http) {
66+
updateWorkspace({ application, http }, id);
67+
}
68+
},
69+
[application, http]
70+
);
71+
72+
const searchResult = useMemo(() => {
73+
if (queryInput) {
74+
const normalizedQuery = queryInput.toLowerCase();
75+
const result = workspaceList.filter((item) => {
76+
return (
77+
item.id.toLowerCase().indexOf(normalizedQuery) > -1 ||
78+
item.name.toLowerCase().indexOf(normalizedQuery) > -1
79+
);
80+
});
81+
return result;
82+
}
83+
return workspaceList;
84+
}, [workspaceList, queryInput]);
85+
5586
const columns = [
5687
{
5788
field: 'name',
@@ -79,46 +110,95 @@ export const WorkspaceList = () => {
79110
isExpander: true,
80111
hasActions: true,
81112
},
113+
{
114+
name: 'Actions',
115+
field: '',
116+
actions: [
117+
{
118+
name: 'Edit',
119+
icon: 'pencil',
120+
type: 'icon',
121+
description: 'edit workspace',
122+
onClick: ({ id }: WorkspaceAttribute) => handleUpdateWorkspace(id),
123+
},
124+
],
125+
},
82126
];
83127

84-
const onTableChange = ({ page, sort }: CriteriaWithPagination<WorkspaceAttribute>) => {
85-
const { field, direction } = sort!;
86-
const { index, size } = page;
128+
const workspaceCreateUrl = useMemo(() => {
129+
if (!application || !http) {
130+
return '';
131+
}
132+
133+
return cleanWorkspaceId(
134+
application.getUrlForApp(WORKSPACE_CREATE_APP_ID, {
135+
absolute: false,
136+
})
137+
);
138+
}, [application, http]);
139+
140+
const debouncedSetQueryInput = useMemo(() => {
141+
return debounce(setQueryInput, 300);
142+
}, [setQueryInput]);
143+
144+
const handleSearchInput: EuiSearchBarProps['onChange'] = ({ query }) => {
145+
debouncedSetQueryInput(query?.text ?? '');
146+
};
147+
148+
const search: EuiSearchBarProps = {
149+
onChange: handleSearchInput,
150+
box: {
151+
incremental: true,
152+
},
153+
toolsRight: [
154+
<EuiButton href={workspaceCreateUrl} key="create_workspace">
155+
Create workspace
156+
</EuiButton>,
157+
],
158+
};
87159

88-
setPageIndex(index);
89-
setPageSize(size);
90-
setSortField(field as 'name' | 'id');
91-
setSortDirection(direction);
160+
const selectionValue: EuiTableSelectionType<WorkspaceAttribute> = {
161+
selectable: () => true,
162+
onSelectionChange: (selection) => {
163+
setSelection(selection);
164+
},
92165
};
93166

94167
return (
95168
<EuiPage paddingSize="none">
96169
<EuiPageBody panelled>
97-
<EuiPageHeader restrictWidth pageTitle="Workspace list" />
170+
<EuiPageHeader
171+
restrictWidth
172+
pageTitle="Workspaces"
173+
description={WORKSPACE_LIST_PAGE_DESCRIPTIOIN}
174+
/>
98175
<EuiPageContent
99176
verticalPosition="center"
100177
horizontalPosition="center"
101178
paddingSize="none"
102-
color="subdued"
179+
panelPaddingSize="l"
103180
hasShadow={false}
104181
style={{ width: '100%', maxWidth: 1000 }}
105182
>
106-
<EuiBasicTable
107-
items={pageOfItems}
183+
<EuiInMemoryTable
184+
items={searchResult}
108185
columns={columns}
109-
pagination={{
110-
pageIndex,
111-
pageSize,
112-
totalItemCount: workspaceList.length,
113-
pageSizeOptions: [5, 10, 20],
114-
}}
186+
itemId="id"
187+
onTableChange={({ page: { index, size } }) =>
188+
setPagination((prev) => {
189+
return { ...prev, pageIndex: index, pageSize: size };
190+
})
191+
}
192+
pagination={pagination}
115193
sorting={{
116194
sort: {
117-
field: sortField,
118-
direction: sortDirection,
195+
field: initialSortField,
196+
direction: initialSortDirection,
119197
},
120198
}}
121-
onChange={onTableChange}
199+
isSelectable={true}
200+
selection={selectionValue}
201+
search={search}
122202
/>
123203
</EuiPageContent>
124204
</EuiPageBody>

src/plugins/workspace/public/components/workspace_menu/workspace_menu.tsx

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
WORKSPACE_OVERVIEW_APP_ID,
3131
} from '../../../common/constants';
3232
import { formatUrlWithWorkspaceId } from '../../../../../core/public/utils';
33+
import { cleanWorkspaceId } from '../../../../../core/public';
3334

3435
interface Props {
3536
getUrlForApp: ApplicationStart['getUrlForApp'];
@@ -117,12 +118,10 @@ export const WorkspaceMenu = ({ basePath, getUrlForApp, workspaces, navigateToUr
117118
key: length.toString(),
118119
onClick: () => {
119120
navigateToUrl(
120-
formatUrlWithWorkspaceId(
121+
cleanWorkspaceId(
121122
getUrlForApp(WORKSPACE_CREATE_APP_ID, {
122123
absolute: false,
123-
}),
124-
currentWorkspace?.id ?? '',
125-
basePath
124+
})
126125
)
127126
);
128127
setPopover(false);
@@ -136,12 +135,10 @@ export const WorkspaceMenu = ({ basePath, getUrlForApp, workspaces, navigateToUr
136135
key: (length + 1).toString(),
137136
onClick: () => {
138137
navigateToUrl(
139-
formatUrlWithWorkspaceId(
138+
cleanWorkspaceId(
140139
getUrlForApp(WORKSPACE_LIST_APP_ID, {
141140
absolute: false,
142-
}),
143-
currentWorkspace?.id ?? '',
144-
basePath
141+
})
145142
)
146143
);
147144
setPopover(false);

0 commit comments

Comments
 (0)