Repository: provectus/kafka-ui
Issue: Brokers, Topic Overview: Show space usage
Labels: type/enhancement
scope/frontend


Issue comments:

Pull request review comments:
I would suggest to add multiline attr instead
```suggestion
  multiline?: boolean;
```
or 
```suggestion
  levelClassName?: 'level-multiline';
```
Done👌 

Patch that resolved the issue:
diff --git a/kafka-ui-react-app/src/components/Brokers/Brokers.tsx b/kafka-ui-react-app/src/components/Brokers/Brokers.tsx
index 4b8eb4683e4..e84cfdbf36f 100644
--- a/kafka-ui-react-app/src/components/Brokers/Brokers.tsx
+++ b/kafka-ui-react-app/src/components/Brokers/Brokers.tsx
@@ -6,6 +6,7 @@ import cx from 'classnames';
 import MetricsWrapper from 'components/common/Dashboard/MetricsWrapper';
 import Indicator from 'components/common/Dashboard/Indicator';
 import Breadcrumb from 'components/common/Breadcrumb/Breadcrumb';
+import BytesFormatted from 'components/common/BytesFormatted/BytesFormatted';
 
 interface Props extends ClusterStats {
   clusterName: ClusterName;
@@ -14,7 +15,7 @@ interface Props extends ClusterStats {
   fetchBrokers: (clusterName: ClusterName) => void;
 }
 
-const Topics: React.FC<Props> = ({
+const Brokers: React.FC<Props> = ({
   clusterName,
   brokerCount,
   activeControllers,
@@ -24,6 +25,7 @@ const Topics: React.FC<Props> = ({
   inSyncReplicasCount,
   outOfSyncReplicasCount,
   underReplicatedPartitionCount,
+  diskUsage,
   fetchClusterStats,
   fetchBrokers,
 }) => {
@@ -73,8 +75,24 @@ const Topics: React.FC<Props> = ({
           {outOfSyncReplicasCount}
         </Indicator>
       </MetricsWrapper>
+
+      <MetricsWrapper multiline title="Disk Usage">
+        {diskUsage?.map((brokerDiskUsage) => (
+          <React.Fragment key={brokerDiskUsage.brokerId}>
+            <Indicator className="is-one-third" label="Broker">
+              {brokerDiskUsage.brokerId}
+            </Indicator>
+            <Indicator className="is-one-third" label="Segment Size" title="">
+              <BytesFormatted value={brokerDiskUsage.segmentSize} />
+            </Indicator>
+            <Indicator className="is-one-third" label="Segment count">
+              {brokerDiskUsage.segmentCount}
+            </Indicator>
+          </React.Fragment>
+        ))}
+      </MetricsWrapper>
     </div>
   );
 };
 
-export default Topics;
+export default Brokers;
diff --git a/kafka-ui-react-app/src/components/Brokers/BrokersContainer.ts b/kafka-ui-react-app/src/components/Brokers/BrokersContainer.ts
index c954c055473..f236d1763c9 100644
--- a/kafka-ui-react-app/src/components/Brokers/BrokersContainer.ts
+++ b/kafka-ui-react-app/src/components/Brokers/BrokersContainer.ts
@@ -31,6 +31,7 @@ const mapStateToProps = (
   underReplicatedPartitionCount: brokerSelectors.getUnderReplicatedPartitionCount(
     state
   ),
+  diskUsage: brokerSelectors.getDiskUsage(state),
 });
 
 const mapDispatchToProps = {
diff --git a/kafka-ui-react-app/src/components/Topics/Details/Overview/Overview.tsx b/kafka-ui-react-app/src/components/Topics/Details/Overview/Overview.tsx
index cce3953f9dd..a9f6a42fa4b 100644
--- a/kafka-ui-react-app/src/components/Topics/Details/Overview/Overview.tsx
+++ b/kafka-ui-react-app/src/components/Topics/Details/Overview/Overview.tsx
@@ -3,6 +3,7 @@ import { ClusterName, TopicName } from 'redux/interfaces';
 import { Topic, TopicDetails } from 'generated-sources';
 import MetricsWrapper from 'components/common/Dashboard/MetricsWrapper';
 import Indicator from 'components/common/Dashboard/Indicator';
+import BytesFormatted from 'components/common/BytesFormatted/BytesFormatted';
 
 interface Props extends Topic, TopicDetails {
   isFetched: boolean;
@@ -22,6 +23,8 @@ const Overview: React.FC<Props> = ({
   partitionCount,
   internal,
   replicationFactor,
+  segmentSize,
+  segmentCount,
   fetchTopicDetails,
 }) => {
   React.useEffect(() => {
@@ -53,6 +56,10 @@ const Overview: React.FC<Props> = ({
             {internal ? 'Internal' : 'External'}
           </span>
         </Indicator>
+        <Indicator label="Segment Size" title="">
+          <BytesFormatted value={segmentSize} />
+        </Indicator>
+        <Indicator label="Segment count">{segmentCount}</Indicator>
       </MetricsWrapper>
       <div className="box">
         <table className="table is-striped is-fullwidth">
diff --git a/kafka-ui-react-app/src/components/common/BytesFormatted/BytesFormatted.tsx b/kafka-ui-react-app/src/components/common/BytesFormatted/BytesFormatted.tsx
new file mode 100644
index 00000000000..a4c24bae672
--- /dev/null
+++ b/kafka-ui-react-app/src/components/common/BytesFormatted/BytesFormatted.tsx
@@ -0,0 +1,22 @@
+import React from 'react';
+
+interface Props {
+  value: string | number | undefined;
+  precision?: number;
+}
+
+const BytesFormatted: React.FC<Props> = ({ value, precision }) => {
+  const formatBytes = React.useCallback(() => {
+    const numVal = typeof value === 'string' ? parseInt(value, 10) : value;
+    if (!numVal) return 0;
+    const pow = Math.floor(Math.log2(numVal) / 10);
+    const multiplier = 10 ** (precision || 2);
+    return (
+      Math.round((numVal * multiplier) / 1024 ** pow) / multiplier +
+      ['Bytes', 'KB', 'MB', 'GB', 'TB'][pow]
+    );
+  }, [value]);
+  return <span>{formatBytes()}</span>;
+};
+
+export default BytesFormatted;
diff --git a/kafka-ui-react-app/src/components/common/Dashboard/Indicator.tsx b/kafka-ui-react-app/src/components/common/Dashboard/Indicator.tsx
index c50bb3da7e0..c4c08d430e5 100644
--- a/kafka-ui-react-app/src/components/common/Dashboard/Indicator.tsx
+++ b/kafka-ui-react-app/src/components/common/Dashboard/Indicator.tsx
@@ -1,13 +1,15 @@
 import React from 'react';
+import cx from 'classnames';
 
 interface Props {
   label: string;
   title?: string;
+  className?: string;
 }
 
-const Indicator: React.FC<Props> = ({ label, title, children }) => {
+const Indicator: React.FC<Props> = ({ label, title, className, children }) => {
   return (
-    <div className="level-item level-left">
+    <div className={cx('level-item', 'level-left', className)}>
       <div title={title || label}>
         <p className="heading">{label}</p>
         <p className="title">{children}</p>
diff --git a/kafka-ui-react-app/src/components/common/Dashboard/MetricsWrapper.tsx b/kafka-ui-react-app/src/components/common/Dashboard/MetricsWrapper.tsx
index e93ba91c717..7e3d7b34a6a 100644
--- a/kafka-ui-react-app/src/components/common/Dashboard/MetricsWrapper.tsx
+++ b/kafka-ui-react-app/src/components/common/Dashboard/MetricsWrapper.tsx
@@ -4,17 +4,21 @@ import cx from 'classnames';
 interface Props {
   title?: string;
   wrapperClassName?: string;
+  multiline?: boolean;
 }
 
 const MetricsWrapper: React.FC<Props> = ({
   title,
   children,
   wrapperClassName,
+  multiline,
 }) => {
   return (
     <div className={cx('box', wrapperClassName)}>
       {title && <h5 className="subtitle is-6">{title}</h5>}
-      <div className="level">{children}</div>
+      <div className={cx('level', multiline ? 'level-multiline' : '')}>
+        {children}
+      </div>
     </div>
   );
 };
diff --git a/kafka-ui-react-app/src/redux/reducers/brokers/selectors.ts b/kafka-ui-react-app/src/redux/reducers/brokers/selectors.ts
index 74b55c95019..eab6d08b50c 100644
--- a/kafka-ui-react-app/src/redux/reducers/brokers/selectors.ts
+++ b/kafka-ui-react-app/src/redux/reducers/brokers/selectors.ts
@@ -43,3 +43,8 @@ export const getUnderReplicatedPartitionCount = createSelector(
   brokersState,
   ({ underReplicatedPartitionCount }) => underReplicatedPartitionCount
 );
+
+export const getDiskUsage = createSelector(
+  brokersState,
+  ({ diskUsage }) => diskUsage
+);
diff --git a/kafka-ui-react-app/src/theme/bulma_overrides.scss b/kafka-ui-react-app/src/theme/bulma_overrides.scss
index 407d9b7b7fc..047d9d822b1 100644
--- a/kafka-ui-react-app/src/theme/bulma_overrides.scss
+++ b/kafka-ui-react-app/src/theme/bulma_overrides.scss
@@ -68,3 +68,13 @@
   from { opacity: 0; }
   to   { opacity: 1; }
 }
+
+.level.level-multiline {
+  flex-wrap: wrap;
+  .level-item.is-one-third {
+    flex-basis: 26%;
+  }
+  .level-item.is-one-third:nth-child(n+4) {
+    margin-top: 10px;
+  }
+}


Test patch: