This is an automated email from the ASF dual-hosted git repository.
arafat2198 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone.git
The following commit(s) were added to refs/heads/master by this push:
new 877504aee1 HDDS-11156. Improve Buckets page UI (#7100)
877504aee1 is described below
commit 877504aee1a5a23eeaa097e159b033018af66c86
Author: Abhishek Pal <[email protected]>
AuthorDate: Tue Sep 3 17:38:26 2024 +0530
HDDS-11156. Improve Buckets page UI (#7100)
---
.../webapps/recon/ozone-recon-web/pnpm-lock.yaml | 134 ++---
.../src/v2/components/search/search.tsx | 4 +
.../src/v2/components/select/multiSelect.tsx | 56 +-
.../src/v2/components/select/singleSelect.tsx | 4 +-
.../src/v2/pages/buckets/buckets.less | 41 ++
.../src/v2/pages/buckets/buckets.tsx | 563 +++++++++++++++++++++
.../src/v2/pages/volumes/volumes.tsx | 8 +-
.../recon/ozone-recon-web/src/v2/routes-v2.tsx | 5 +
.../ozone-recon-web/src/v2/types/bucket.types.ts | 20 +-
9 files changed, 742 insertions(+), 93 deletions(-)
diff --git
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/pnpm-lock.yaml
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/pnpm-lock.yaml
index ebbc4e2219..3c472d5f79 100644
---
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/pnpm-lock.yaml
+++
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/pnpm-lock.yaml
@@ -153,7 +153,7 @@ packages:
dependencies:
'@ant-design/colors': 6.0.0
'@ant-design/icons-svg': 4.4.2
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
classnames: 2.5.1
lodash: 4.17.21
rc-util: 5.43.0([email protected])([email protected])
@@ -166,7 +166,7 @@ packages:
peerDependencies:
react: '>=16.9.0'
dependencies:
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
classnames: 2.5.1
json2mq: 0.2.0
lodash: 4.17.21
@@ -187,11 +187,11 @@ packages:
'@babel/highlight': 7.24.7
picocolors: 1.0.1
- /@babel/[email protected]:
- resolution: {integrity:
sha512-abd43wyLfbWoxC6ahM8xTkqLpGB2iWBVyuKC9/srhFunCd1SDNrV1s72bBpK4hLj8KLzHBBcOblvLQZBNw9r3w==}
+ /@babel/[email protected]:
+ resolution: {integrity:
sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/types': 7.25.4
+ '@babel/types': 7.25.6
'@jridgewell/gen-mapping': 0.3.5
'@jridgewell/trace-mapping': 0.3.25
jsesc: 2.5.2
@@ -201,8 +201,8 @@ packages:
resolution: {integrity:
sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/traverse': 7.25.4
- '@babel/types': 7.25.4
+ '@babel/traverse': 7.25.6
+ '@babel/types': 7.25.6
transitivePeerDependencies:
- supports-color
dev: false
@@ -225,16 +225,16 @@ packages:
js-tokens: 4.0.0
picocolors: 1.0.1
- /@babel/[email protected]:
- resolution: {integrity:
sha512-nq+eWrOgdtu3jG5Os4TQP3x3cLA8hR8TvJNjD8vnPa20WGycimcparWnLK4jJhElTK6SDyuJo1weMKO/5LpmLA==}
+ /@babel/[email protected]:
+ resolution: {integrity:
sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==}
engines: {node: '>=6.0.0'}
hasBin: true
dependencies:
- '@babel/types': 7.25.4
+ '@babel/types': 7.25.6
dev: false
- /@babel/[email protected]:
- resolution: {integrity:
sha512-DSgLeL/FNcpXuzav5wfYvHCGvynXkJbn3Zvc3823AEe9nPwW9IK4UoCSS5yGymmQzN0pCPvivtgS6/8U2kkm1w==}
+ /@babel/[email protected]:
+ resolution: {integrity:
sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==}
engines: {node: '>=6.9.0'}
dependencies:
regenerator-runtime: 0.14.1
@@ -244,27 +244,27 @@ packages:
engines: {node: '>=6.9.0'}
dependencies:
'@babel/code-frame': 7.24.7
- '@babel/parser': 7.25.4
- '@babel/types': 7.25.4
+ '@babel/parser': 7.25.6
+ '@babel/types': 7.25.6
dev: false
- /@babel/[email protected]:
- resolution: {integrity:
sha512-VJ4XsrD+nOvlXyLzmLzUs/0qjFS4sK30te5yEFlvbbUNEgKaVb2BHZUpAL+ttLPQAHNrsI3zZisbfha5Cvr8vg==}
+ /@babel/[email protected]:
+ resolution: {integrity:
sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/code-frame': 7.24.7
- '@babel/generator': 7.25.5
- '@babel/parser': 7.25.4
+ '@babel/generator': 7.25.6
+ '@babel/parser': 7.25.6
'@babel/template': 7.25.0
- '@babel/types': 7.25.4
+ '@babel/types': 7.25.6
debug: 4.3.6
globals: 11.12.0
transitivePeerDependencies:
- supports-color
dev: false
- /@babel/[email protected]:
- resolution: {integrity:
sha512-zQ1ijeeCXVEh+aNL0RlmkPkG8HUiDcU2pzQQFjtbntgAczRASFzj4H+6+bV+dy1ntKR14I/DypeuRG1uma98iQ==}
+ /@babel/[email protected]:
+ resolution: {integrity:
sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/helper-string-parser': 7.24.8
@@ -295,7 +295,7 @@ packages:
peerDependencies:
react: '>=16.3.0'
dependencies:
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
'@emotion/cache': 10.0.29
'@emotion/css': 10.0.27
'@emotion/serialize': 0.11.16
@@ -1186,7 +1186,7 @@ packages:
engines: {node: '>=12'}
dependencies:
'@babel/code-frame': 7.24.7
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
'@types/aria-query': 5.0.4
aria-query: 5.1.3
chalk: 4.1.2
@@ -1215,7 +1215,7 @@ packages:
react: <18.0.0
react-dom: <18.0.0
dependencies:
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
'@testing-library/dom': 8.20.1
'@types/react-dom': 16.8.4
react: 16.14.0
@@ -1674,7 +1674,7 @@ packages:
'@ant-design/colors': 5.1.1
'@ant-design/icons': 4.8.3([email protected])([email protected])
'@ant-design/react-slick': 0.28.4([email protected])
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
array-tree-filter: 2.1.0
classnames: 2.5.1
copy-to-clipboard: 3.3.3
@@ -1853,7 +1853,7 @@ packages:
/[email protected]:
resolution: {integrity:
sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==}
dependencies:
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
cosmiconfig: 6.0.0
resolve: 1.22.8
dev: false
@@ -2370,7 +2370,7 @@ packages:
resolution: {integrity:
sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==}
engines: {node: '>=0.11'}
dependencies:
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
dev: false
/[email protected]:
@@ -2547,7 +2547,7 @@ packages:
/[email protected]:
resolution: {integrity:
sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==}
dependencies:
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
csstype: 3.1.3
dev: false
@@ -3514,7 +3514,7 @@ packages:
/[email protected]:
resolution: {integrity:
sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==}
dependencies:
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
loose-envify: 1.4.0
resolve-pathname: 3.0.0
tiny-invariant: 1.3.3
@@ -5088,7 +5088,7 @@ packages:
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
classnames: 2.5.1
dom-align: 1.12.4
rc-util: 5.43.0([email protected])([email protected])
@@ -5103,7 +5103,7 @@ packages:
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
array-tree-filter: 2.1.0
rc-trigger: 5.3.4([email protected])([email protected])
rc-util: 5.43.0([email protected])([email protected])
@@ -5118,7 +5118,7 @@ packages:
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
classnames: 2.5.1
react: 16.14.0
react-dom: 16.14.0([email protected])
@@ -5130,7 +5130,7 @@ packages:
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
classnames: 2.5.1
rc-motion: 2.9.2([email protected])([email protected])
rc-util: 5.43.0([email protected])([email protected])
@@ -5145,7 +5145,7 @@ packages:
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
classnames: 2.5.1
rc-motion: 2.9.2([email protected])([email protected])
rc-util: 5.43.0([email protected])([email protected])
@@ -5159,7 +5159,7 @@ packages:
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
classnames: 2.5.1
rc-util: 5.43.0([email protected])([email protected])
react: 16.14.0
@@ -5172,7 +5172,7 @@ packages:
react: '*'
react-dom: '*'
dependencies:
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
classnames: 2.5.1
rc-trigger: 5.3.4([email protected])([email protected])
react: 16.14.0
@@ -5186,7 +5186,7 @@ packages:
react: '>= 16.9.0'
react-dom: '>= 16.9.0'
dependencies:
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
async-validator: 3.5.2
rc-util: 5.43.0([email protected])([email protected])
react: 16.14.0
@@ -5199,7 +5199,7 @@ packages:
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
classnames: 2.5.1
rc-dialog: 8.5.3([email protected])([email protected])
rc-util: 5.43.0([email protected])([email protected])
@@ -5213,7 +5213,7 @@ packages:
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
classnames: 2.5.1
rc-util: 5.43.0([email protected])([email protected])
react: 16.14.0
@@ -5226,7 +5226,7 @@ packages:
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
classnames: 2.5.1
rc-menu: 8.10.8([email protected])([email protected])
rc-textarea: 0.3.7([email protected])([email protected])
@@ -5242,7 +5242,7 @@ packages:
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
classnames: 2.5.1
mini-store: 3.0.6([email protected])([email protected])
rc-motion: 2.9.2([email protected])([email protected])
@@ -5260,7 +5260,7 @@ packages:
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
classnames: 2.5.1
rc-util: 5.43.0([email protected])([email protected])
react: 16.14.0
@@ -5274,7 +5274,7 @@ packages:
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
classnames: 2.5.1
rc-motion: 2.9.2([email protected])([email protected])
rc-util: 5.43.0([email protected])([email protected])
@@ -5288,7 +5288,7 @@ packages:
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
classnames: 2.5.1
rc-resize-observer: 1.4.0([email protected])([email protected])
rc-util: 5.43.0([email protected])([email protected])
@@ -5302,7 +5302,7 @@ packages:
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
classnames: 2.5.1
react: 16.14.0
react-dom: 16.14.0([email protected])
@@ -5315,7 +5315,7 @@ packages:
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
classnames: 2.5.1
date-fns: 2.30.0
dayjs: 1.11.13
@@ -5333,7 +5333,7 @@ packages:
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
classnames: 2.5.1
react: 16.14.0
react-dom: 16.14.0([email protected])
@@ -5346,7 +5346,7 @@ packages:
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
classnames: 2.5.1
rc-util: 5.43.0([email protected])([email protected])
react: 16.14.0
@@ -5359,7 +5359,7 @@ packages:
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
classnames: 2.5.1
rc-util: 5.43.0([email protected])([email protected])
react: 16.14.0
@@ -5374,7 +5374,7 @@ packages:
react: '*'
react-dom: '*'
dependencies:
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
classnames: 2.5.1
rc-motion: 2.9.2([email protected])([email protected])
rc-overflow: 1.3.2([email protected])([email protected])
@@ -5392,7 +5392,7 @@ packages:
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
classnames: 2.5.1
rc-tooltip: 5.0.2([email protected])([email protected])
rc-util: 5.43.0([email protected])([email protected])
@@ -5408,7 +5408,7 @@ packages:
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
classnames: 2.5.1
rc-util: 5.43.0([email protected])([email protected])
react: 16.14.0
@@ -5421,7 +5421,7 @@ packages:
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
classnames: 2.5.1
rc-util: 5.43.0([email protected])([email protected])
react: 16.14.0
@@ -5435,7 +5435,7 @@ packages:
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
classnames: 2.5.1
rc-resize-observer: 1.4.0([email protected])([email protected])
rc-util: 5.43.0([email protected])([email protected])
@@ -5451,7 +5451,7 @@ packages:
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
classnames: 2.5.1
rc-dropdown: 3.2.5([email protected])([email protected])
rc-menu: 8.10.8([email protected])([email protected])
@@ -5467,7 +5467,7 @@ packages:
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
classnames: 2.5.1
rc-resize-observer: 1.4.0([email protected])([email protected])
rc-util: 5.43.0([email protected])([email protected])
@@ -5482,7 +5482,7 @@ packages:
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
rc-trigger: 5.3.4([email protected])([email protected])
react: 16.14.0
react-dom: 16.14.0([email protected])
@@ -5494,7 +5494,7 @@ packages:
react: '*'
react-dom: '*'
dependencies:
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
classnames: 2.5.1
rc-select: 12.1.13([email protected])([email protected])
rc-tree: 4.1.5([email protected])([email protected])
@@ -5510,7 +5510,7 @@ packages:
react: '*'
react-dom: '*'
dependencies:
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
classnames: 2.5.1
rc-motion: 2.9.2([email protected])([email protected])
rc-util: 5.43.0([email protected])([email protected])
@@ -5526,7 +5526,7 @@ packages:
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
classnames: 2.5.1
rc-align: 4.0.15([email protected])([email protected])
rc-motion: 2.9.2([email protected])([email protected])
@@ -5541,7 +5541,7 @@ packages:
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
classnames: 2.5.1
rc-util: 5.43.0([email protected])([email protected])
react: 16.14.0
@@ -5554,7 +5554,7 @@ packages:
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
react: 16.14.0
react-dom: 16.14.0([email protected])
react-is: 18.3.1
@@ -5567,7 +5567,7 @@ packages:
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
classnames: 2.5.1
rc-resize-observer: 1.4.0([email protected])([email protected])
rc-util: 5.43.0([email protected])([email protected])
@@ -5620,7 +5620,7 @@ packages:
peerDependencies:
react: '>=15'
dependencies:
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
history: 4.10.1
loose-envify: 1.4.0
prop-types: 15.8.1
@@ -5635,7 +5635,7 @@ packages:
peerDependencies:
react: '>=15'
dependencies:
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
history: 4.10.1
hoist-non-react-statics: 3.3.2
loose-envify: 1.4.0
@@ -5653,7 +5653,7 @@ packages:
react: ^16.8.0 || ^17.0.0
react-dom: ^16.8.0 || ^17.0.0
dependencies:
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
'@emotion/cache': 10.0.29
'@emotion/core': 10.3.1([email protected])
'@emotion/css': 10.0.27
@@ -5673,7 +5673,7 @@ packages:
react: '>=16.6.0'
react-dom: '>=16.6.0'
dependencies:
- '@babel/runtime': 7.25.4
+ '@babel/runtime': 7.25.6
dom-helpers: 5.2.1
loose-envify: 1.4.0
prop-types: 15.8.1
diff --git
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/search/search.tsx
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/search/search.tsx
index 21d4341787..8cac2a9c04 100644
---
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/search/search.tsx
+++
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/search/search.tsx
@@ -23,6 +23,7 @@ import { Option } from '@/v2/components/select/singleSelect';
// ------------- Types -------------- //
type SearchProps = {
+ disabled?: boolean;
searchColumn?: string;
searchInput: string;
searchOptions?: Option[];
@@ -39,6 +40,7 @@ type SearchProps = {
// ------------- Component -------------- //
const Search: React.FC<SearchProps> = ({
+ disabled = false,
searchColumn,
searchInput = '',
searchOptions = [],
@@ -48,6 +50,7 @@ const Search: React.FC<SearchProps> = ({
const selectFilter = searchColumn
? (<Select
+ disabled={disabled}
defaultValue={searchColumn}
options={searchOptions}
onChange={onChange} />)
@@ -55,6 +58,7 @@ const Search: React.FC<SearchProps> = ({
return (
<Input
+ disabled={disabled}
placeholder='Enter Search text'
allowClear={true}
value={searchInput}
diff --git
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/select/multiSelect.tsx
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/select/multiSelect.tsx
index 7a6b494aae..07b3f9eafa 100644
---
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/select/multiSelect.tsx
+++
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/select/multiSelect.tsx
@@ -22,7 +22,8 @@ import {
Props as ReactSelectProps,
components,
OptionProps,
- ValueType
+ ValueType,
+ ValueContainerProps
} from 'react-select';
import { selectStyles } from "@/v2/constants/select.constants";
@@ -45,6 +46,27 @@ interface MultiSelectProps extends ReactSelectProps<Option,
true> {
}
// ------------- Component -------------- //
+
+const Option: React.FC<OptionProps<Option, true>> = (props) => {
+ return (
+ <div>
+ <components.Option
+ {...props}>
+ <input
+ type='checkbox'
+ checked={props.isSelected}
+ style={{
+ marginRight: '8px',
+ accentColor: '#1AA57A'
+ }}
+ onChange={() => null} />
+ <label>{props.label}</label>
+ </components.Option>
+ </div>
+ )
+}
+
+
const MultiSelect: React.FC<MultiSelectProps> = ({
options = [],
selected = [],
@@ -58,24 +80,20 @@ const MultiSelect: React.FC<MultiSelectProps> = ({
...props
}) => {
- const Option: React.FC<OptionProps<Option, true>> = (props) => {
+ const ValueContainer = ({ children, ...props }: ValueContainerProps<Option,
true>) => {
return (
- <div>
- <components.Option
- {...props}>
- <input
- type='checkbox'
- checked={props.isSelected}
- style={{
- marginRight: '8px',
- accentColor: '#1AA57A'
- }}
- onChange={() => null} />
- <label>{props.label}</label>
- </components.Option>
- </div>
- )
- }
+ <components.ValueContainer {...props}>
+ {React.Children.map(children, (child) => (
+ ((child as React.ReactElement<any, string
+ | React.JSXElementConstructor<any>>
+ | React.ReactPortal)?.type as
React.JSXElementConstructor<any>)).name === "DummyInput"
+ ? child
+ : null
+ )}
+ {placeholder}: {selected.length} selected
+ </components.ValueContainer>
+ );
+ };
return (
<ReactSelect
@@ -89,10 +107,12 @@ const MultiSelect: React.FC<MultiSelectProps> = ({
classNamePrefix='multi-select'
options={options}
components={{
+ ValueContainer,
Option
}}
placeholder={placeholder}
value={selected}
+ isOptionDisabled={(option) => option.value === fixedColumn}
onChange={(selected: ValueType<Option, true>) => {
if (selected?.length === options.length) return onChange!(options);
return onChange!(selected);
diff --git
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/select/singleSelect.tsx
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/select/singleSelect.tsx
index 41ab03f598..1d02b40733 100644
---
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/select/singleSelect.tsx
+++
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/select/singleSelect.tsx
@@ -50,7 +50,7 @@ const SingleSelect: React.FC<SingleSelectProps> = ({
const ValueContainer = ({ children, ...props }: ValueContainerProps<Option,
false>) => {
- const selectedLimit = props.getValue() as Option[];
+ const selectedValue = props.getValue() as Option[];
return (
<components.ValueContainer {...props}>
{React.Children.map(children, (child) => (
@@ -60,7 +60,7 @@ const SingleSelect: React.FC<SingleSelectProps> = ({
? child
: null
)}
- Limit: {selectedLimit[0]?.label ?? ''}
+ {placeholder}: {selectedValue[0]?.label ?? ''}
</components.ValueContainer>
);
};
diff --git
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/buckets/buckets.less
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/buckets/buckets.less
new file mode 100644
index 0000000000..8f4c8ffaf9
--- /dev/null
+++
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/buckets/buckets.less
@@ -0,0 +1,41 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+.content-div {
+ min-height: unset;
+
+ .table-header-section {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+
+ .table-filter-section {
+ font-size: 14px;
+ font-weight: normal;
+ display: flex;
+ column-gap: 8px;
+ padding: 16px 8px;
+ }
+ }
+
+ .tag-block {
+ display: flex;
+ column-gap: 8px;
+ padding: 0px 8px 16px 8px;
+ }
+}
diff --git
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/buckets/buckets.tsx
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/buckets/buckets.tsx
new file mode 100644
index 0000000000..bd8950e54c
--- /dev/null
+++
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/buckets/buckets.tsx
@@ -0,0 +1,563 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import React, { useEffect, useState } from 'react';
+import moment from 'moment';
+import { Table, Tag } from 'antd';
+import {
+ ColumnProps,
+ ColumnsType,
+ TablePaginationConfig
+} from 'antd/es/table';
+import {
+ CheckCircleOutlined,
+ CloseCircleOutlined,
+ CloudServerOutlined,
+ FileUnknownOutlined,
+ HddOutlined,
+ LaptopOutlined,
+ SaveOutlined
+} from '@ant-design/icons';
+import { ValueType } from 'react-select';
+import { useLocation } from 'react-router-dom';
+
+import QuotaBar from '@/components/quotaBar/quotaBar';
+import AutoReloadPanel from '@/components/autoReloadPanel/autoReloadPanel';
+import AclPanel from '@/v2/components/aclDrawer/aclDrawer';
+import Search from '@/v2/components/search/search';
+import MultiSelect from '@/v2/components/select/multiSelect';
+import SingleSelect, { Option } from '@/v2/components/select/singleSelect';
+
+import { AutoReloadHelper } from '@/utils/autoReloadHelper';
+import { AxiosGetHelper } from "@/utils/axiosRequestHelper";
+import { nullAwareLocaleCompare, showDataFetchError } from '@/utils/common';
+import { useDebounce } from '@/v2/hooks/debounce.hook';
+
+import {
+ Bucket,
+ BucketLayout,
+ BucketLayoutTypeList,
+ BucketResponse,
+ BucketsState,
+ BucketStorage,
+ BucketStorageTypeList
+} from '@/v2/types/bucket.types';
+
+import './buckets.less';
+
+
+const LIMIT_OPTIONS: Option[] = [
+ {
+ label: '1000',
+ value: '1000'
+ },
+ {
+ label: '5000',
+ value: '5000'
+ },
+ {
+ label: '10000',
+ value: '10000'
+ },
+ {
+ label: '20000',
+ value: '20000'
+ }
+]
+
+const renderIsVersionEnabled = (isVersionEnabled: boolean) => {
+ return isVersionEnabled
+ ? <CheckCircleOutlined
+ style={{ color: '#1da57a' }}
+ className='icon-success' />
+ : <CloseCircleOutlined className='icon-neutral' />
+};
+
+const renderStorageType = (bucketStorage: BucketStorage) => {
+ const bucketStorageIconMap: Record<BucketStorage, React.ReactElement> = {
+ RAM_DISK: <LaptopOutlined />,
+ SSD: <SaveOutlined />,
+ DISK: <HddOutlined />,
+ ARCHIVE: <CloudServerOutlined />
+ };
+ const icon = bucketStorage in bucketStorageIconMap
+ ? bucketStorageIconMap[bucketStorage]
+ : <FileUnknownOutlined />;
+ return <span>{icon} {bucketStorage}</span>;
+};
+
+const renderBucketLayout = (bucketLayout: BucketLayout) => {
+ const bucketLayoutColorMap = {
+ FILE_SYSTEM_OPTIMIZED: 'green',
+ OBJECT_STORE: 'orange',
+ LEGACY: 'blue'
+ };
+ const color = bucketLayout in bucketLayoutColorMap ?
+ bucketLayoutColorMap[bucketLayout] : '';
+ return <Tag color={color}>{bucketLayout}</Tag>;
+};
+
+const SearchableColumnOpts = [{
+ label: 'Bucket',
+ value: 'name'
+}, {
+ label: 'Volume',
+ value: 'volumeName'
+}]
+
+const COLUMNS: ColumnsType<Bucket> = [
+ {
+ title: 'Bucket',
+ dataIndex: 'name',
+ key: 'name',
+ sorter: (a: Bucket, b: Bucket) => a.name.localeCompare(b.name),
+ defaultSortOrder: 'ascend' as const
+ },
+ {
+ title: 'Volume',
+ dataIndex: 'volumeName',
+ key: 'volumeName',
+ sorter: (a: Bucket, b: Bucket) => a.volumeName.localeCompare(b.volumeName),
+ defaultSortOrder: 'ascend' as const
+ },
+ {
+ title: 'Owner',
+ dataIndex: 'owner',
+ key: 'owner',
+ sorter: (a: Bucket, b: Bucket) => nullAwareLocaleCompare(a.owner, b.owner)
+ },
+ {
+ title: 'Versioning',
+ dataIndex: 'versioning',
+ key: 'isVersionEnabled',
+ render: (isVersionEnabled: boolean) =>
renderIsVersionEnabled(isVersionEnabled)
+ },
+ {
+ title: 'Storage Type',
+ dataIndex: 'storageType',
+ key: 'storageType',
+ filterMultiple: true,
+ filters: BucketStorageTypeList.map(state => ({ text: state, value: state
})),
+ onFilter: (value, record: Bucket) => record.storageType === value,
+ sorter: (a: Bucket, b: Bucket) =>
a.storageType.localeCompare(b.storageType),
+ render: (storageType: BucketStorage) => renderStorageType(storageType)
+ },
+ {
+ title: 'Bucket Layout',
+ dataIndex: 'bucketLayout',
+ key: 'bucketLayout',
+ filterMultiple: true,
+ filters: BucketLayoutTypeList.map(state => ({ text: state, value: state
})),
+ onFilter: (value, record: Bucket) => record.bucketLayout === value,
+ sorter: (a: Bucket, b: Bucket) =>
a.bucketLayout.localeCompare(b.bucketLayout),
+ render: (bucketLayout: BucketLayout) => renderBucketLayout(bucketLayout)
+ },
+ {
+ title: 'Creation Time',
+ dataIndex: 'creationTime',
+ key: 'creationTime',
+ sorter: (a: Bucket, b: Bucket) => a.creationTime - b.creationTime,
+ render: (creationTime: number) => {
+ return creationTime > 0 ? moment(creationTime).format('ll LTS') : 'NA';
+ }
+ },
+ {
+ title: 'Modification Time',
+ dataIndex: 'modificationTime',
+ key: 'modificationTime',
+ sorter: (a: Bucket, b: Bucket) => a.modificationTime - b.modificationTime,
+ render: (modificationTime: number) => {
+ return modificationTime > 0 ? moment(modificationTime).format('ll LTS')
: 'NA';
+ }
+ },
+ {
+ title: 'Storage Capacity',
+ key: 'quotaCapacityBytes',
+ sorter: (a: Bucket, b: Bucket) => a.usedBytes - b.usedBytes,
+ render: (text: string, record: Bucket) => (
+ <QuotaBar
+ quota={record.quotaInBytes}
+ used={record.usedBytes}
+ quotaType='size'
+ />
+ )
+ },
+ {
+ title: 'Namespace Capacity',
+ key: 'namespaceCapacity',
+ sorter: (a: Bucket, b: Bucket) => a.usedNamespace - b.usedNamespace,
+ render: (text: string, record: Bucket) => (
+ <QuotaBar
+ quota={record.quotaInNamespace}
+ used={record.usedNamespace}
+ quotaType='namespace'
+ />
+ )
+ },
+ {
+ title: 'Source Volume',
+ dataIndex: 'sourceVolume',
+ key: 'sourceVolume',
+ render: (sourceVolume: string) => {
+ return sourceVolume ? sourceVolume : 'NA';
+ }
+ },
+ {
+ title: 'Source Bucket',
+ dataIndex: 'sourceBucket',
+ key: 'sourceBucket',
+ render: (sourceBucket: string) => {
+ return sourceBucket ? sourceBucket : 'NA';
+ }
+ }
+];
+
+const defaultColumns = COLUMNS.map(column => ({
+ label: column.title as string,
+ value: column.key as string
+}));
+
+function getVolumeBucketMap(data: Bucket[]) {
+ const volumeBucketMap = data.reduce((
+ map: Map<string, Set<Bucket>>,
+ currentBucket
+ ) => {
+ const volume = currentBucket.volumeName;
+ if (map.has(volume)) {
+ const buckets = Array.from(map.get(volume)!);
+ map.set(volume, new Set([...buckets, currentBucket]));
+ } else {
+ map.set(volume, new Set<Bucket>().add(currentBucket));
+ }
+ return map;
+ }, new Map<string, Set<Bucket>>());
+ return volumeBucketMap;
+}
+
+function getFilteredBuckets(
+ selectedVolumes: Option[],
+ bucketsMap: Map<string, Set<Bucket>>
+) {
+ let selectedBuckets: Bucket[] = [];
+ selectedVolumes.forEach(selectedVolume => {
+ if (bucketsMap.has(selectedVolume.value)
+ && bucketsMap.get(selectedVolume.value)) {
+ selectedBuckets = [
+ ...selectedBuckets,
+ ...Array.from(bucketsMap.get(selectedVolume.value)!)
+ ];
+ }
+ });
+
+ return selectedBuckets;
+}
+
+const Buckets: React.FC<{}> = () => {
+
+ let cancelSignal: AbortController;
+
+ const [state, setState] = useState<BucketsState>({
+ totalCount: 0,
+ lastUpdated: 0,
+ columnOptions: defaultColumns,
+ volumeBucketMap: new Map<string, Set<Bucket>>(),
+ bucketsUnderVolume: [],
+ volumeOptions: [],
+ });
+ const [loading, setLoading] = useState<boolean>(false);
+ const [selectedColumns, setSelectedColumns] =
useState<Option[]>(defaultColumns);
+ const [selectedVolumes, setSelectedVolumes] = useState<Option[]>([]);
+ const [selectedLimit, setSelectedLimit] = useState<Option>(LIMIT_OPTIONS[0]);
+ const [searchTerm, setSearchTerm] = useState<string>('');
+ const [showPanel, setShowPanel] = useState<boolean>(false);
+ const [searchColumn, setSearchColumn] = useState<'name' |
'volumeName'>('name');
+ const [currentRow, setCurrentRow] = useState<Bucket | Record<string,
never>>({})
+
+ const debouncedSearch = useDebounce(searchTerm, 300);
+ const { search } = useLocation();
+
+ const paginationConfig: TablePaginationConfig = {
+ showTotal: (total: number, range) => `${range[0]}-${range[1]} of ${total}
buckets`,
+ showSizeChanger: true
+ };
+
+ function getVolumeSearchParam() {
+ return new URLSearchParams(search).get('volume');
+ };
+
+ function getFilteredData(data: Bucket[]) {
+ return data.filter(
+ (bucket: Bucket) => bucket[searchColumn].includes(debouncedSearch)
+ );
+ }
+
+ function handleVolumeChange(selected: ValueType<Option, true>) {
+ const { volumeBucketMap } = state;
+ const volumeSelections = (selected as Option[]);
+ let selectedBuckets: Bucket[] = [];
+
+ if (volumeSelections?.length > 0) {
+ selectedBuckets = getFilteredBuckets(volumeSelections, volumeBucketMap)
+ }
+
+ setSelectedVolumes(volumeSelections);
+ setState({
+ ...state,
+ bucketsUnderVolume: selectedBuckets
+ });
+ };
+
+ function handleAclLinkClick(bucket: Bucket) {
+ setCurrentRow(bucket);
+ setShowPanel(true);
+ }
+
+ function filterSelectedColumns() {
+ const columnKeys = selectedColumns.map((column) => column.value);
+ return COLUMNS.filter(
+ (column) => columnKeys.indexOf(column.key as string) >= 0
+ )
+ }
+
+ function addAclColumn() {
+ // Inside the class component to access the React internal state
+ const aclLinkColumn: ColumnProps<Bucket> = {
+ title: 'ACLs',
+ dataIndex: 'acls',
+ key: 'acls',
+ render: (_: any, record: Bucket) => {
+ return (
+ <a
+ key='acl'
+ onClick={() => {
+ handleAclLinkClick(record);
+ }}
+ >
+ Show ACL
+ </a>
+ );
+ }
+ };
+
+ if (COLUMNS.length > 0 && COLUMNS[COLUMNS.length - 1].key !== 'acls') {
+ // Push the ACL column for initial
+ COLUMNS.push(aclLinkColumn);
+ } else {
+ // Replace old ACL column with new ACL column with correct reference
+ // e.g. After page is reloaded / redirect from other page
+ COLUMNS[COLUMNS.length - 1] = aclLinkColumn;
+ }
+
+ if (defaultColumns.length > 0 && defaultColumns[defaultColumns.length -
1].label !== 'acls') {
+ defaultColumns.push({
+ label: aclLinkColumn.title as string,
+ value: aclLinkColumn.key as string
+ });
+ }
+ };
+
+ function handleColumnChange(selected: ValueType<Option, true>) {
+ setSelectedColumns(selected as Option[]);
+ }
+
+ function handleLimitChange(selected: ValueType<Option, false>) {
+ setSelectedLimit(selected as Option);
+ }
+
+ const loadData = () => {
+ setLoading(true);
+ const { request, controller } = AxiosGetHelper(
+ '/api/v1/buckets',
+ cancelSignal,
+ '',
+ { limit: selectedLimit.value }
+ );
+ cancelSignal = controller;
+ request.then(response => {
+ const bucketsResponse: BucketResponse = response.data;
+ const totalCount = bucketsResponse.totalCount;
+ const buckets: Bucket[] = bucketsResponse.buckets;
+
+ const dataSource: Bucket[] = buckets?.map(bucket => {
+ return {
+ volumeName: bucket.volumeName,
+ name: bucket.name,
+ versioning: bucket.versioning,
+ storageType: bucket.storageType,
+ bucketLayout: bucket.bucketLayout,
+ creationTime: bucket.creationTime,
+ modificationTime: bucket.modificationTime,
+ sourceVolume: bucket.sourceVolume,
+ sourceBucket: bucket.sourceBucket,
+ usedBytes: bucket.usedBytes,
+ usedNamespace: bucket.usedNamespace,
+ quotaInBytes: bucket.quotaInBytes,
+ quotaInNamespace: bucket.quotaInNamespace,
+ owner: bucket.owner,
+ acls: bucket.acls
+ };
+ }) ?? [];
+
+ const volumeBucketMap: Map<string, Set<Bucket>> =
getVolumeBucketMap(dataSource);
+
+ // Set options for volume selection dropdown
+ const volumeOptions: Option[] = Array.from(
+ volumeBucketMap.keys()
+ ).map(k => ({
+ label: k,
+ value: k
+ }));
+
+ setLoading(false);
+
+ setSelectedVolumes((prevState) => {
+ if (prevState.length === 0) return volumeOptions;
+ return prevState;
+ });
+
+ setState({
+ ...state,
+ totalCount: totalCount,
+ volumeBucketMap: volumeBucketMap,
+ volumeOptions: volumeOptions,
+ lastUpdated: Number(moment())
+ });
+ }).catch(error => {
+ setLoading(false);
+ showDataFetchError(error.toString());
+ });
+ }
+
+ let autoReloadHelper: AutoReloadHelper = new AutoReloadHelper(loadData);
+
+ useEffect(() => {
+ autoReloadHelper.startPolling();
+ addAclColumn();
+ const initialVolume = getVolumeSearchParam();
+ if (initialVolume) {
+ setSelectedVolumes([{
+ label: initialVolume,
+ value: initialVolume
+ }]);
+ }
+ loadData();
+
+ return (() => {
+ autoReloadHelper.stopPolling();
+ cancelSignal && cancelSignal.abort();
+ })
+ }, []);
+
+ useEffect(() => {
+ // If the data is fetched, we need to regenerate the columns
+ // To make sure the filters are properly applied
+ setState({
+ ...state,
+ bucketsUnderVolume: getFilteredBuckets(
+ selectedVolumes,
+ state.volumeBucketMap
+ )
+ });
+ }, [state.volumeBucketMap])
+
+ // If limit changes, load new data
+ useEffect(() => {
+ loadData();
+ }, [selectedLimit.value]);
+
+ const {
+ lastUpdated, columnOptions,
+ volumeOptions, bucketsUnderVolume
+ } = state;
+
+ return (
+ <>
+ <div className='page-header-v2'>
+ Buckets
+ <AutoReloadPanel
+ isLoading={loading}
+ lastRefreshed={lastUpdated}
+ togglePolling={autoReloadHelper.handleAutoReloadToggle}
+ onReload={loadData}
+ />
+ </div>
+ <div style={{ padding: '24px' }}>
+ <div className='content-div'>
+ <div className='table-header-section'>
+ <div className='table-filter-section'>
+ <MultiSelect
+ options={volumeOptions}
+ defaultValue={selectedVolumes}
+ selected={selectedVolumes}
+ placeholder='Volumes'
+ onChange={handleVolumeChange}
+ fixedColumn=''
+ onTagClose={() => { }}
+ columnLength={volumeOptions.length} />
+ <MultiSelect
+ options={columnOptions}
+ defaultValue={selectedColumns}
+ selected={selectedColumns}
+ placeholder='Columns'
+ onChange={handleColumnChange}
+ onTagClose={() => { }}
+ fixedColumn='name'
+ columnLength={COLUMNS.length} />
+ <SingleSelect
+ options={LIMIT_OPTIONS}
+ defaultValue={selectedLimit}
+ placeholder='Limit'
+ onChange={handleLimitChange} />
+ </div>
+ <Search
+ disabled={bucketsUnderVolume?.length < 1}
+ searchOptions={SearchableColumnOpts}
+ searchInput={searchTerm}
+ searchColumn={searchColumn}
+ onSearchChange={
+ (e: React.ChangeEvent<HTMLInputElement>) =>
setSearchTerm(e.target.value)
+ }
+ onChange={(value) => {
+ setSearchTerm('');
+ setSearchColumn(value as 'name' | 'volumeName');
+ }} />
+ </div>
+ <div>
+ <Table
+ dataSource={getFilteredData(bucketsUnderVolume)}
+ columns={filterSelectedColumns()}
+ loading={loading}
+ rowKey='volume'
+ pagination={paginationConfig}
+ scroll={{ x: 'max-content', scrollToFirstRowOnChange: true }}
+ locale={{ filterTitle: '' }}
+ />
+ </div>
+ </div>
+ <AclPanel
+ visible={showPanel}
+ acls={currentRow.acls}
+ entityName={currentRow.name}
+ entityType='Bucket'
+ onClose={() => setShowPanel(false)} />
+ </div>
+ </>
+ )
+}
+
+export default Buckets;
\ No newline at end of file
diff --git
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/volumes/volumes.tsx
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/volumes/volumes.tsx
index 6c323fc949..605883caff 100644
---
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/volumes/volumes.tsx
+++
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/volumes/volumes.tsx
@@ -195,7 +195,7 @@ const Volumes: React.FC<{}> = () => {
request.then(response => {
const volumesResponse: VolumesResponse = response.data;
const volumes: Volume[] = volumesResponse.volumes;
- const data: Volume[] = volumes.map(volume => {
+ const data: Volume[] = volumes?.map(volume => {
return {
volume: volume.volume,
owner: volume.owner,
@@ -207,7 +207,7 @@ const Volumes: React.FC<{}> = () => {
usedNamespace: volume.usedNamespace,
acls: volume.acls
};
- });
+ }) ?? [];
setState({
...state,
@@ -308,8 +308,7 @@ const Volumes: React.FC<{}> = () => {
placeholder='Columns'
onChange={handleColumnChange}
onTagClose={handleTagClose}
- fixedColumn='Volume'
- isOptionDisabled={(option) => option.value === 'volume'}
+ fixedColumn='volume'
columnLength={COLUMNS.length} />
<SingleSelect
options={LIMIT_OPTIONS}
@@ -318,6 +317,7 @@ const Volumes: React.FC<{}> = () => {
onChange={handleLimitChange} />
</div>
<Search
+ disabled={data?.length < 1}
searchOptions={SearchableColumnOpts}
searchInput={searchTerm}
searchColumn={searchColumn}
diff --git
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/routes-v2.tsx
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/routes-v2.tsx
index 5d71024616..8a37ef9c51 100644
---
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/routes-v2.tsx
+++
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/routes-v2.tsx
@@ -19,6 +19,7 @@ import { lazy } from 'react';
const Overview = lazy(() => import('@/v2/pages/overview/overview'));
const Volumes = lazy(() => import('@/v2/pages/volumes/volumes'))
+const Buckets = lazy(() => import('@/v2/pages/buckets/buckets'));
export const routesV2 = [
{
@@ -28,5 +29,9 @@ export const routesV2 = [
{
path: '/Volumes',
component: Volumes
+ },
+ {
+ path: '/Buckets',
+ component: Buckets
}
];
diff --git
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/types/bucket.types.ts
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/types/bucket.types.ts
index 8b2fd0c694..5cfc89d85e 100644
---
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/types/bucket.types.ts
+++
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/types/bucket.types.ts
@@ -17,6 +17,8 @@
*/
import { Acl } from "@/v2/types/acl.types";
+import { Option } from "@/v2/components/select/singleSelect";
+import { Option as MultiOption } from "@/v2/components/select/multiSelect";
// Corresponds to OzoneManagerProtocolProtos.StorageTypeProto
export const BucketStorageTypeList = [
@@ -38,8 +40,8 @@ export type BucketLayout = typeof
BucketLayoutTypeList[number];
export type Bucket = {
volumeName: string;
- bucketName: string;
- isVersionEnabled: boolean;
+ name: string;
+ versioning: boolean;
storageType: BucketStorage;
creationTime: number;
modificationTime: number;
@@ -53,3 +55,17 @@ export type Bucket = {
acls?: Acl[];
bucketLayout: BucketLayout;
}
+
+export type BucketResponse = {
+ totalCount: number;
+ buckets: Bucket[];
+}
+
+export type BucketsState = {
+ totalCount: number;
+ lastUpdated: number;
+ columnOptions: MultiOption[];
+ volumeBucketMap: Map<string, Set<Bucket>>;
+ bucketsUnderVolume: Bucket[];
+ volumeOptions: MultiOption[];
+}
\ No newline at end of file
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]