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 ebad350033 HDDS-11155. Improve Volumes page UI (#7048)
ebad350033 is described below

commit ebad3500332fc2639615b36dde2680bdb3c5d5ca
Author: Abhishek Pal <[email protected]>
AuthorDate: Tue Aug 20 10:50:22 2024 +0530

    HDDS-11155. Improve Volumes page UI (#7048)
---
 .../webapps/recon/ozone-recon-web/src/app.tsx      |  12 +-
 .../recon/ozone-recon-web/src/utils/common.tsx     |   5 +-
 .../src/v2/components/aclDrawer/aclDrawer.tsx      | 119 +++++++
 .../src/v2/components/eChart/eChart.tsx            |   2 +-
 .../loader/loader.tsx}                             |  28 +-
 .../src/v2/components/search/search.tsx            |  70 ++++
 .../src/v2/components/select/columnTag.tsx         |  67 ++++
 .../src/v2/components/select/multiSelect.tsx       | 104 ++++++
 .../src/v2/components/select/singleSelect.tsx      |  87 +++++
 .../{routes-v2.tsx => constants/acl.constants.tsx} |  25 +-
 .../src/v2/constants/select.constants.tsx          |  62 ++++
 .../v2/{routes-v2.tsx => hooks/debounce.hook.tsx}  |  23 +-
 .../src/v2/pages/volumes/volumes.less              |  41 +++
 .../src/v2/pages/volumes/volumes.tsx               | 353 +++++++++++++++++++++
 .../recon/ozone-recon-web/src/v2/routes-v2.tsx     |  10 +-
 .../src/v2/{routes-v2.tsx => types/acl.types.ts}   |  35 +-
 .../ozone-recon-web/src/v2/types/bucket.types.ts   |  55 ++++
 .../ozone-recon-web/src/v2/types/volume.types.ts   |  44 +++
 18 files changed, 1104 insertions(+), 38 deletions(-)

diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/app.tsx
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/app.tsx
index c52fe9efa9..0ad6aa3f17 100644
--- 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/app.tsx
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/app.tsx
@@ -16,7 +16,7 @@
  * limitations under the License.
  */
 
-import React from 'react';
+import React, { Suspense } from 'react';
 
 import { Switch as AntDSwitch, Layout } from 'antd';
 import NavBar from './components/navBar/navBar';
@@ -27,6 +27,8 @@ import { routesV2 } from '@/v2/routes-v2';
 import { MakeRouteWithSubRoutes } from '@/makeRouteWithSubRoutes';
 import classNames from 'classnames';
 
+import Loader from '@/v2/components/loader/loader';
+
 import './app.less';
 
 const {
@@ -80,9 +82,11 @@ class App extends React.Component<Record<string, object>, 
IAppState> {
                   <Redirect to='/Overview' />
                 </Route>
                 {(enableNewUI)
-                  ? routesV2.map(
-                    (route, index) => <MakeRouteWithSubRoutes key={index} 
{...route} />
-                  )
+                  ? <Suspense fallback={<Loader/>}>
+                    {routesV2.map(
+                      (route, index) => <MakeRouteWithSubRoutes key={index} 
{...route} />
+                    )}
+                  </Suspense>
                   : routes.map(
                     (route, index) => <MakeRouteWithSubRoutes key={index} 
{...route} />
                   )
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/utils/common.tsx
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/utils/common.tsx
index 6886fd189f..f641b8797d 100644
--- 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/utils/common.tsx
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/utils/common.tsx
@@ -44,9 +44,8 @@ const showInfoNotification = (title: string, description: 
string) => {
 
 export const showDataFetchError = (error: string) => {
   let title = 'Error while fetching data';
-  if (error.includes('CanceledError')) {
-    error = 'Previous request cancelled because context changed'
-  }
+  
+  if (error.includes('CanceledError')) return;
   if (error.includes('metadata')) {
     title = 'Metadata Initialization:';
     showInfoNotification(title, error);
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/aclDrawer/aclDrawer.tsx
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/aclDrawer/aclDrawer.tsx
new file mode 100644
index 0000000000..af0931c17f
--- /dev/null
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/aclDrawer/aclDrawer.tsx
@@ -0,0 +1,119 @@
+/*
+ * 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 { Table, Drawer, Tag } from 'antd';
+
+import { AclRightsColorMap, AclIdColorMap } from 
'@/v2/constants/acl.constants';
+import { Acl, ACLIdentity, ACLIdentityTypeList } from '@/v2/types/acl.types';
+import { ColumnType } from 'antd/es/table';
+
+// ------------- Types -------------- //
+type AclDrawerProps = {
+  visible: boolean;
+  acls: Acl[] | undefined;
+  entityName: string;
+  entityType: string;
+  onClose: () => void;
+}
+
+
+// ------------- Component -------------- //
+const AclPanel: React.FC<AclDrawerProps> = ({
+  visible,
+  acls,
+  entityType,
+  entityName,
+  onClose
+}) => {
+  const [isVisible, setIsVisible] = useState<boolean>(false);
+
+  useEffect(() => {
+    setIsVisible(visible);
+  }, [visible]);
+
+  const renderAclList = (_: string, acl: Acl) => {
+    return acl.aclList.map(aclRight => (
+      <Tag key={aclRight} color={AclRightsColorMap[aclRight as keyof typeof 
AclRightsColorMap]}>
+        {aclRight}
+      </Tag>
+    ))
+  }
+
+  const renderAclIdentityType = (acl: string) => {
+    return (
+      <Tag color={AclIdColorMap[acl as keyof typeof AclIdColorMap]}>
+        {acl}
+      </Tag>
+    )
+  }
+
+  const COLUMNS: ColumnType<Acl>[] = [
+    {
+      title: 'Name',
+      dataIndex: 'name',
+      key: 'name',
+      sorter: (a: Acl, b: Acl) => a.name.localeCompare(b.name),
+    },
+    {
+      title: 'ACL Type',
+      dataIndex: 'type',
+      key: 'type',
+      filterMultiple: true,
+      filters: ACLIdentityTypeList.map(state => ({ text: state, value: state 
})),
+      onFilter: (value: ACLIdentity, record: Acl) => (record.type === value),
+      sorter: (a: Acl, b: Acl) => a.type.localeCompare(b.type),
+      render: renderAclIdentityType
+    },
+    {
+      title: 'ACL Scope',
+      dataIndex: 'scope',
+      key: 'scope',
+    },
+    {
+      title: 'ACLs',
+      dataIndex: 'aclList',
+      key: 'acls',
+      render: renderAclList
+    }
+  ];
+
+  return (
+    <div className='site-drawer-render-in-current-wrapper'>
+      <Drawer
+        title={`ACL for ${entityType} ${entityName}`}
+        placement='right'
+        width='40%'
+        closable={true}
+        visible={isVisible}
+        getContainer={false}
+        style={{ position: 'absolute' }}
+        onClose={onClose}
+      >
+        <Table
+          dataSource={acls}
+          rowKey='name'
+          locale={{ filterTitle: '' }}
+          columns={COLUMNS}>
+        </Table>
+      </Drawer>
+    </div>
+  );
+};
+
+export default AclPanel;
\ No newline at end of file
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/eChart/eChart.tsx
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/eChart/eChart.tsx
index 8be22fcc9f..79fa076033 100644
--- 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/eChart/eChart.tsx
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/eChart/eChart.tsx
@@ -83,7 +83,7 @@ const EChart = ({
     }
   }, [loading, theme]); // If we switch theme we should put chart in loading 
mode, and also if loading changes i.e completes then hide loader
 
-  return <div ref={chartRef} style={{ width: "100em", height: "50em", margin: 
'auto', ...style }} />;
+  return <div ref={chartRef} style={{ width: "50vw", height: "25vh", margin: 
'auto', ...style }} />;
 }
 
 export default EChart;
\ No newline at end of file
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/components/loader/loader.tsx
similarity index 60%
copy from 
hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/routes-v2.tsx
copy to 
hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/loader/loader.tsx
index 4cdd700d50..b05eaa5f0a 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/components/loader/loader.tsx
@@ -16,11 +16,25 @@
  * limitations under the License.
  */
 
-import Overview  from '@/v2/pages/overview/overview';
+import React from "react"
+import { Spin } from "antd"
+import { LoadingOutlined } from "@ant-design/icons"
 
-export const routesV2: IRoute[] = [
-  {
-    path: '/Overview',
-    component: Overview
-  }
-];
+// ------------- Constants -------------- //
+const loaderStyle: React.CSSProperties = {
+  height: '100%',
+  width: '100%',
+  textAlign: 'center',
+  paddingTop: '25%'
+}
+
+// ------------- Component -------------- //
+const Loader: React.FC = () => {
+  return (
+    <div style={loaderStyle}>
+      <Spin indicator={<LoadingOutlined style={{ color: '#1AA57A', fontSize: 
48}} spin/>}/>
+    </div>
+  )
+}
+
+export default Loader;
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
new file mode 100644
index 0000000000..21d4341787
--- /dev/null
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/search/search.tsx
@@ -0,0 +1,70 @@
+/*
+ * 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 from 'react';
+import { Input, Select } from 'antd';
+
+import { Option } from '@/v2/components/select/singleSelect';
+
+// ------------- Types -------------- //
+type SearchProps = {
+  searchColumn?: string;
+  searchInput: string;
+  searchOptions?: Option[];
+  onSearchChange: (
+    arg0: React.ChangeEvent<HTMLInputElement>
+  ) => void;
+  onChange: (
+    value: string,
+    //OptionType, OptionGroupData and OptionData are not
+    //currently exported by AntD hence set to any
+    option: any
+  ) => void;
+}
+
+// ------------- Component -------------- //
+const Search: React.FC<SearchProps> = ({
+  searchColumn,
+  searchInput = '',
+  searchOptions = [],
+  onSearchChange = () => {},
+  onChange = () => {}   // Assign default value as a void function
+}) => {
+
+  const selectFilter = searchColumn
+    ? (<Select
+      defaultValue={searchColumn}
+      options={searchOptions}
+      onChange={onChange} />)
+    : null
+
+  return (
+    <Input
+      placeholder='Enter Search text'
+      allowClear={true}
+      value={searchInput}
+      addonBefore={selectFilter}
+      onChange={onSearchChange}
+      size='middle'
+      style={{
+        maxWidth: 400
+      }}/>
+  )
+}
+
+export default Search;
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/select/columnTag.tsx
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/select/columnTag.tsx
new file mode 100644
index 0000000000..f367504286
--- /dev/null
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/select/columnTag.tsx
@@ -0,0 +1,67 @@
+/*
+ * 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 from "react";
+import { Tag } from "antd";
+import { createPortal } from "react-dom";
+
+
+// ------------- Types -------------- //
+/**
+ *  Due to design decisions we are currently not using the Tags
+ * Until we reach a concensus on a better way to display the filter
+ * Keeping the code in case we require it in the future
+ */
+export type TagProps = {
+  label: string;
+  closable: boolean;
+  tagRef: React.RefObject<HTMLDivElement>;
+  onClose: (arg0: string) => void;
+}
+
+// ------------- Component -------------- //
+const ColumnTag: React.FC<TagProps> = ({
+  label = '',
+  closable = true,
+  tagRef = null,
+  onClose = () => {} // Assign default value as void funciton
+}) => {
+  const onPreventMouseDown = (event: React.MouseEvent<HTMLSpanElement>) => {
+    // By default when clickin on the tags the text will get selected
+    // which might interfere with user experience as people would want to 
close tags
+    // but accidentally select tag text. Hence we prevent this behaviour.
+    event.preventDefault();
+    event.stopPropagation();
+  };
+
+  if (!tagRef?.current) return null;
+
+  return createPortal(
+    <Tag
+      key={label}
+      onMouseDown={onPreventMouseDown}
+      closable={closable}
+      onClose={() => (onClose(label))}
+      style={{marginRight: 3}}>
+        {label}
+    </Tag>,
+    tagRef.current
+  );
+}
+
+export default ColumnTag;
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
new file mode 100644
index 0000000000..7a6b494aae
--- /dev/null
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/select/multiSelect.tsx
@@ -0,0 +1,104 @@
+/*
+ * 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 from "react";
+import {
+  default as ReactSelect,
+  Props as ReactSelectProps,
+  components,
+  OptionProps,
+  ValueType
+} from 'react-select';
+
+import { selectStyles } from "@/v2/constants/select.constants";
+
+
+// ------------- Types -------------- //
+export type Option = {
+  label: string;
+  value: string;
+}
+
+interface MultiSelectProps extends ReactSelectProps<Option, true> {
+  options: Option[];
+  selected: Option[];
+  placeholder: string;
+  fixedColumn: string;
+  columnLength: number;
+  onChange: (arg0: ValueType<Option, true>) => void;
+  onTagClose: (arg0: string) => void;
+}
+
+// ------------- Component -------------- //
+const MultiSelect: React.FC<MultiSelectProps> = ({
+  options = [],
+  selected = [],
+  maxSelected = 5,
+  placeholder = 'Columns',
+  fixedColumn,
+  columnLength,
+  tagRef,
+  onTagClose = () => { },  // Assign default value as a void function
+  onChange = () => { },  // Assign default value as a void function
+  ...props
+}) => {
+
+  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>
+    )
+  }
+
+  return (
+    <ReactSelect
+    {...props}
+    isMulti={true}
+    closeMenuOnSelect={false}
+    hideSelectedOptions={false}
+    isClearable={false}
+    isSearchable={false}
+    controlShouldRenderValue={false}
+    classNamePrefix='multi-select'
+    options={options}
+    components={{
+      Option
+    }}
+    placeholder={placeholder}
+    value={selected}
+    onChange={(selected: ValueType<Option, true>) => {
+      if (selected?.length === options.length) return onChange!(options);
+      return onChange!(selected);
+    }}
+    styles={selectStyles} />
+  )
+}
+
+export default MultiSelect;
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
new file mode 100644
index 0000000000..41ab03f598
--- /dev/null
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/components/select/singleSelect.tsx
@@ -0,0 +1,87 @@
+/*
+ * 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 from "react";
+import Select, {
+  Props as ReactSelectProps,
+  components,
+  ValueType,
+  ValueContainerProps,
+  StylesConfig
+} from 'react-select';
+
+import { selectStyles } from "@/v2/constants/select.constants";
+
+
+// ------------- Types -------------- //
+export type Option = {
+  label: string;
+  value: string;
+}
+
+interface SingleSelectProps extends ReactSelectProps<Option, false> {
+  options: Option[];
+  placeholder: string;
+  onChange: (arg0: ValueType<Option, false>) => void;
+}
+
+// ------------- Component -------------- //
+const SingleSelect: React.FC<SingleSelectProps> = ({
+  options = [],
+  placeholder = 'Limit',
+  onChange = () => { },  // Assign default value as a void function
+  ...props  // Desctructure other select props
+}) => {
+
+
+  const ValueContainer = ({ children, ...props }: ValueContainerProps<Option, 
false>) => {
+    const selectedLimit = props.getValue() as Option[];
+    return (
+      <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
+        )}
+        Limit: {selectedLimit[0]?.label ?? ''}
+      </components.ValueContainer>
+    );
+  };
+
+  return (
+    <Select
+      {...props}
+      isClearable={false}
+      closeMenuOnSelect={true}
+      classNamePrefix='single-select'
+      isSearchable={false}
+      options={options}
+      components={{
+        ValueContainer
+      }}
+      placeholder={placeholder}
+      onChange={(selected: ValueType<Option, false>) => {
+        return onChange!(selected);
+      }}
+      styles={selectStyles as StylesConfig<Option, false>} />
+  );
+}
+
+export default SingleSelect;
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/constants/acl.constants.tsx
similarity index 71%
copy from 
hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/routes-v2.tsx
copy to 
hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/constants/acl.constants.tsx
index 4cdd700d50..d1cc54dab2 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/constants/acl.constants.tsx
@@ -16,11 +16,22 @@
  * limitations under the License.
  */
 
-import Overview  from '@/v2/pages/overview/overview';
+export const AclIdColorMap = {
+  USER: 'green',
+  GROUP: 'blue',
+  WORLD: 'magenta',
+  ANONYMOUS: 'gray',
+  CLIENT_IP: 'gold'
+};
 
-export const routesV2: IRoute[] = [
-  {
-    path: '/Overview',
-    component: Overview
-  }
-];
+export const AclRightsColorMap = {
+  READ: 'green',
+  WRITE: 'blue',
+  CREATE: 'orange',
+  LIST: 'magenta',
+  DELETE: 'red',
+  READ_ACL: 'lime',
+  WRITE_ACL: 'purple',
+  ALL: 'gold',
+  NONE: 'gray'
+};
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/constants/select.constants.tsx
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/constants/select.constants.tsx
new file mode 100644
index 0000000000..465c2533bc
--- /dev/null
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/constants/select.constants.tsx
@@ -0,0 +1,62 @@
+/*
+* 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 { StylesConfig } from "react-select";
+import { Option } from "@/v2/components/select/multiSelect";
+
+export const selectStyles: StylesConfig<Option, true> = {
+  control: (baseStyles, state) => ({
+    ...baseStyles,
+    minWidth: 200,
+    boxShadow: 'none',
+    borderRadius: '2px',
+    borderColor: state.isFocused ? '#1AA57A' : '#E6E6E6',
+    '&:hover': {
+      borderColor: '#1AA57A'
+    }
+  }),
+  option: (baseStyles, state) => ({
+    ...baseStyles,
+    display: 'flex',
+    padding: '5px 12px',
+    alignItems: 'center',
+    color: state.isSelected ? '#1AA57A' : '#262626',
+    backgroundColor: state.isSelected ? '#EDF7F4' : '#FFFFFF',
+    '&:active': {
+      color: state.isSelected ? '#FFFFFF' : '#262626',
+      backgroundColor: state.isSelected ? '#64BDA1' : '#EDF7F4'
+    }
+  }),
+  menuList: (baseStyles) => ({
+    ...baseStyles,
+    boxShadow: 'rgba(50, 50, 93, 0.25) 0px 6px 12px -2px, rgba(0, 0, 0, 0.3) 
0px 3px 7px -3px',
+    padding: 0
+  }),
+  menu: (baseStyles) => ({
+    ...baseStyles,
+    height: 100
+  }),
+  placeholder: (baseStyles) => ({
+    ...baseStyles,
+    color: 'rgba(0, 0, 0, 0.85)'
+
+  }),
+  indicatorSeparator: () => ({
+    display: 'none'
+  })
+}
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/hooks/debounce.hook.tsx
similarity index 63%
copy from 
hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/routes-v2.tsx
copy to 
hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/hooks/debounce.hook.tsx
index 4cdd700d50..e66dbd5679 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/hooks/debounce.hook.tsx
@@ -16,11 +16,20 @@
  * limitations under the License.
  */
 
-import Overview  from '@/v2/pages/overview/overview';
+import React from 'react';
 
-export const routesV2: IRoute[] = [
-  {
-    path: '/Overview',
-    component: Overview
-  }
-];
+export function useDebounce<T>(value: T, timeout: number): T {
+  const [debounceValue, setDebounceValue] = React.useState<T>(value);
+
+  React.useEffect(() => {
+    const timeoutHandler = setTimeout(() => {
+      setDebounceValue(value);
+    }, timeout);
+
+    return () => {
+      clearTimeout(timeoutHandler);
+    }
+  }, [value, timeout]); // Need to set new timeout anytime the value or 
timeout duration changes
+
+  return debounceValue;
+}
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/volumes/volumes.less
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/volumes/volumes.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/volumes/volumes.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/volumes/volumes.tsx
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/volumes/volumes.tsx
new file mode 100644
index 0000000000..a5918ac6ce
--- /dev/null
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/pages/volumes/volumes.tsx
@@ -0,0 +1,353 @@
+/*
+ * 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 } from 'antd';
+import { Link } from 'react-router-dom';
+import {
+  TablePaginationConfig,
+  ColumnsType
+} from 'antd/es/table';
+import { ValueType } from 'react-select/src/types';
+
+import QuotaBar from '@/components/quotaBar/quotaBar';
+import AclPanel from '@/v2/components/aclDrawer/aclDrawer';
+import AutoReloadPanel from '@/components/autoReloadPanel/autoReloadPanel';
+import MultiSelect, { Option } from '@/v2/components/select/multiSelect';
+import SingleSelect from '@/v2/components/select/singleSelect';
+import Search from '@/v2/components/search/search';
+
+import { byteToSize, showDataFetchError } from '@/utils/common';
+import { AutoReloadHelper } from '@/utils/autoReloadHelper';
+import { AxiosGetHelper } from "@/utils/axiosRequestHelper";
+import { useDebounce } from '@/v2/hooks/debounce.hook';
+
+import {
+  Volume,
+  VolumesState,
+  VolumesResponse
+} from '@/v2/types/volume.types';
+
+import './volumes.less';
+
+const SearchableColumnOpts = [
+  {
+    label: 'Volume',
+    value: 'volume'
+  },
+  {
+    label: 'Owner',
+    value: 'owner'
+  },
+  {
+    label: 'Admin',
+    value: 'admin'
+  }
+]
+
+const LIMIT_OPTIONS: Option[] = [
+  { label: '1000', value: '1000' },
+  { label: '5000', value: "5000" },
+  { label: '10000', value: "10000" },
+  { label: '20000', value: "20000" }
+]
+
+const Volumes: React.FC<{}> = () => {
+
+  let cancelSignal: AbortController;
+
+  const COLUMNS: ColumnsType<Volume> = [
+    {
+      title: 'Volume',
+      dataIndex: 'volume',
+      key: 'volume',
+      sorter: (a: Volume, b: Volume) => a.volume.localeCompare(b.volume),
+      defaultSortOrder: 'ascend' as const,
+      width: '15%'
+    },
+    {
+      title: 'Owner',
+      dataIndex: 'owner',
+      key: 'owner',
+      sorter: (a: Volume, b: Volume) => a.owner.localeCompare(b.owner)
+    },
+    {
+      title: 'Admin',
+      dataIndex: 'admin',
+      key: 'admin',
+      sorter: (a: Volume, b: Volume) => a.admin.localeCompare(b.admin)
+    },
+    {
+      title: 'Creation Time',
+      dataIndex: 'creationTime',
+      key: 'creationTime',
+      sorter: (a: Volume, b: Volume) => 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: Volume, b: Volume) => a.modificationTime - 
b.modificationTime,
+      render: (modificationTime: number) => {
+        return modificationTime > 0 ? moment(modificationTime).format('ll 
LTS') : 'NA';
+      }
+    },
+    {
+      title: 'Quota (Size)',
+      dataIndex: 'quotaInBytes',
+      key: 'quotaInBytes',
+      render: (quotaInBytes: number) => {
+        return quotaInBytes && quotaInBytes !== -1 ? byteToSize(quotaInBytes, 
3) : 'NA';
+      }
+    },
+    {
+      title: 'Namespace Capacity',
+      key: 'namespaceCapacity',
+      sorter: (a: Volume, b: Volume) => a.usedNamespace - b.usedNamespace,
+      render: (text: string, record: Volume) => (
+        <QuotaBar
+          quota={record.quotaInNamespace}
+          used={record.usedNamespace}
+          quotaType='namespace'
+        />
+      )
+    },
+    {
+      title: 'Actions',
+      key: 'actions',
+      render: (_: any, record: Volume) => {
+        const searchParams = new URLSearchParams();
+        searchParams.append('volume', record.volume);
+
+        return (
+          <>
+            <Link
+              key="listBuckets"
+              to={`/Buckets?${searchParams.toString()}`}
+              style={{
+                marginRight: '16px'
+              }}>
+              Show buckets
+            </Link>
+            <a
+              key='acl'
+              onClick={() => handleAclLinkClick(record)}>
+              Show ACL
+            </a>
+          </>
+        );
+      }
+    }
+  ];
+
+  const defaultColumns = COLUMNS.map(column => ({
+    label: column.title as string,
+    value: column.key as string,
+  }));
+
+  const [state, setState] = useState<VolumesState>({
+    data: [],
+    lastUpdated: 0,
+    columnOptions: defaultColumns,
+    currentRow: {}
+  });
+  const [loading, setLoading] = useState<boolean>(false);
+  const [selectedColumns, setSelectedColumns] = 
useState<Option[]>(defaultColumns);
+  const [selectedLimit, setSelectedLimit] = useState<Option>(LIMIT_OPTIONS[0]);
+  const [searchColumn, setSearchColumn] = useState<'volume' | 'owner' | 
'admin'>('volume');
+  const [searchTerm, setSearchTerm] = useState<string>('');
+  const [showPanel, setShowPanel] = useState<boolean>(false);
+
+  const debouncedSearch = useDebounce(searchTerm, 300);
+
+  const loadData = () => {
+    setLoading(true);
+
+    const { request, controller } = AxiosGetHelper(
+      '/api/v1/volumes',
+      cancelSignal,
+      "",
+      { limit: selectedLimit.value }
+    );
+
+    cancelSignal = controller;
+    request.then(response => {
+      const volumesResponse: VolumesResponse = response.data;
+      const volumes: Volume[] = volumesResponse.volumes;
+      const data: Volume[] = volumes.map(volume => {
+        return {
+          volume: volume.volume,
+          owner: volume.owner,
+          admin: volume.admin,
+          creationTime: volume.creationTime,
+          modificationTime: volume.modificationTime,
+          quotaInBytes: volume.quotaInBytes,
+          quotaInNamespace: volume.quotaInNamespace,
+          usedNamespace: volume.usedNamespace,
+          acls: volume.acls
+        };
+      });
+
+      setState({
+        ...state,
+        data,
+        lastUpdated: Number(moment()),
+      });
+      setLoading(false);
+    }).catch(error => {
+      setLoading(false);
+      showDataFetchError(error.toString());
+    });
+  };
+
+  let autoReloadHelper: AutoReloadHelper = new AutoReloadHelper(loadData);
+
+  useEffect(() => {
+    loadData();
+    autoReloadHelper.startPolling();
+
+    // Component will unmount
+    return (() => {
+      autoReloadHelper.stopPolling();
+      cancelSignal && cancelSignal.abort();
+    })
+  }, []);
+
+  // If limit changes, load new data
+  useEffect(() => {
+    loadData();
+  }, [selectedLimit.value]);
+
+  function handleColumnChange(selected: ValueType<Option, true>) {
+    setSelectedColumns(selected as Option[]);
+  }
+
+  function handleLimitChange(selected: ValueType<Option, false>) {
+    setSelectedLimit(selected as Option);
+  }
+
+  function handleTagClose(label: string) {
+    setSelectedColumns(
+      selectedColumns.filter((column) => column.label !== label)
+    )
+  }
+
+
+  function handleAclLinkClick(volume: Volume) {
+    setState({
+      ...state,
+      currentRow: volume
+    });
+    setShowPanel(true);
+  }
+
+  function filterSelectedColumns() {
+    const columnKeys = selectedColumns.map((column) => column.value);
+    return COLUMNS.filter(
+      (column) => columnKeys.indexOf(column.key as string) >= 0
+    )
+  }
+
+  function getFilteredData(data: Volume[]) {
+    return data.filter(
+      (volume: Volume) => volume[searchColumn].includes(debouncedSearch)
+    );
+  }
+
+
+  const paginationConfig: TablePaginationConfig = {
+    showTotal: (total: number, range) => `${range[0]}-${range[1]} of ${total} 
volumes`,
+    showSizeChanger: true
+  };
+
+  const {
+    data, lastUpdated,
+    columnOptions, currentRow
+  } = state;
+
+  return (
+    <>
+      <div className='page-header-v2'>
+        Volumes
+        <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={columnOptions}
+                defaultValue={selectedColumns}
+                selected={selectedColumns}
+                placeholder='Columns'
+                onChange={handleColumnChange}
+                onTagClose={handleTagClose}
+                fixedColumn='Volume'
+                isOptionDisabled={(option) => option.value === 'volume'}
+                columnLength={COLUMNS.length} />
+              <SingleSelect
+                options={LIMIT_OPTIONS}
+                defaultValue={selectedLimit}
+                placeholder='Limit'
+                onChange={handleLimitChange} />
+            </div>
+            <Search
+              searchOptions={SearchableColumnOpts}
+              searchInput={searchTerm}
+              searchColumn={searchColumn}
+              onSearchChange={
+                (e: React.ChangeEvent<HTMLInputElement>) => 
setSearchTerm(e.target.value)
+              }
+              onChange={(value) => {
+                setSearchTerm('');
+                setSearchColumn(value as 'volume' | 'owner' | 'admin');
+              }} />
+          </div>
+          <div>
+            <Table
+              dataSource={getFilteredData(data)}
+              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.volume}
+          entityType='Volume'
+          onClose={() => setShowPanel(false)}/>
+      </div>
+    </>
+  );
+}
+
+export default Volumes;
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 4cdd700d50..5d71024616 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
@@ -15,12 +15,18 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+import { lazy } from 'react';
 
-import Overview  from '@/v2/pages/overview/overview';
+const Overview = lazy(() => import('@/v2/pages/overview/overview'));
+const Volumes = lazy(() => import('@/v2/pages/volumes/volumes'))
 
-export const routesV2: IRoute[] = [
+export const routesV2 = [
   {
     path: '/Overview',
     component: Overview
+  },
+  {
+    path: '/Volumes',
+    component: Volumes
   }
 ];
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/types/acl.types.ts
similarity index 63%
copy from 
hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/routes-v2.tsx
copy to 
hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/types/acl.types.ts
index 4cdd700d50..33cd047d18 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/types/acl.types.ts
@@ -16,11 +16,32 @@
  * limitations under the License.
  */
 
-import Overview  from '@/v2/pages/overview/overview';
+export const ACLIdentityTypeList = [
+  'USER',
+  'GROUP',
+  'WORLD',
+  'ANONYMOUS',
+  'CLIENT_IP'
+] as const;
+export type ACLIdentity = typeof ACLIdentityTypeList[number];
 
-export const routesV2: IRoute[] = [
-  {
-    path: '/Overview',
-    component: Overview
-  }
-];
+export const ACLRightList = [
+  'READ',
+  'WRITE',
+  'CREATE',
+  'LIST',
+  'DELETE',
+  'READ_ACL',
+  'WRITE_ACL',
+  'ALL',
+  'NONE'
+] as const;
+export type ACLRight = typeof ACLRightList[number];
+
+
+export type Acl = {
+  type: string;
+  name: string;
+  scope: string;
+  aclList: string[];
+}
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
new file mode 100644
index 0000000000..8b2fd0c694
--- /dev/null
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/types/bucket.types.ts
@@ -0,0 +1,55 @@
+/*
+ * 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 { Acl } from "@/v2/types/acl.types";
+
+// Corresponds to OzoneManagerProtocolProtos.StorageTypeProto
+export const BucketStorageTypeList = [
+  'RAM_DISK',
+  'SSD',
+  'DISK',
+  'ARCHIVE'
+] as const;
+export type BucketStorage = typeof BucketStorageTypeList[number];
+
+// Corresponds to OzoneManagerProtocolProtos.BucketLayoutProto
+export const BucketLayoutTypeList = [
+  'FILE_SYSTEM_OPTIMIZED',
+  'OBJECT_STORE',
+  'LEGACY'
+] as const;
+export type BucketLayout = typeof BucketLayoutTypeList[number];
+
+
+export type Bucket = {
+  volumeName: string;
+  bucketName: string;
+  isVersionEnabled: boolean;
+  storageType: BucketStorage;
+  creationTime: number;
+  modificationTime: number;
+  sourceVolume?: string;
+  sourceBucket?: string;
+  usedBytes: number;
+  usedNamespace: number;
+  quotaInBytes: number;
+  quotaInNamespace: number;
+  owner: string;
+  acls?: Acl[];
+  bucketLayout: BucketLayout;
+}
diff --git 
a/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/types/volume.types.ts
 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/types/volume.types.ts
new file mode 100644
index 0000000000..67f007706a
--- /dev/null
+++ 
b/hadoop-ozone/recon/src/main/resources/webapps/recon/ozone-recon-web/src/v2/types/volume.types.ts
@@ -0,0 +1,44 @@
+/*
+* 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 { Acl } from "@/v2/types/acl.types";
+import { Option } from "@/v2/components/select/multiSelect";
+
+export type Volume = {
+  volume: string;
+  owner: string;
+  admin: string;
+  creationTime: number;
+  modificationTime: number;
+  quotaInBytes: number;
+  quotaInNamespace: number;
+  usedNamespace: number;
+  acls?: Acl[];
+}
+
+export type VolumesResponse = {
+  totalCount: number;
+  volumes: Volume[];
+}
+
+export type VolumesState = {
+  data: Volume[];
+  lastUpdated: number;
+  columnOptions: Option[];
+  currentRow: Volume | Record<string, never>;
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to