瀏覽代碼

Use back link instead of breadcrumbs (#2430)

* Use back link instead of breadcrumbs

* Unify topic pages

* Unify topic page actions

* fix

* Remove unused test
Oleg Shur 2 年之前
父節點
當前提交
8d3f098385
共有 93 個文件被更改,包括 607 次插入991 次删除
  1. 6 1
      kafka-ui-react-app/src/components/Brokers/Broker/Broker.tsx
  2. 2 17
      kafka-ui-react-app/src/components/Brokers/Brokers.tsx
  3. 1 1
      kafka-ui-react-app/src/components/Brokers/BrokersList/BrokersList.tsx
  4. 39 73
      kafka-ui-react-app/src/components/Cluster/Cluster.tsx
  5. 4 27
      kafka-ui-react-app/src/components/Connect/Connect.tsx
  6. 6 1
      kafka-ui-react-app/src/components/Connect/Details/DetailsPage.tsx
  7. 10 2
      kafka-ui-react-app/src/components/Connect/New/New.tsx
  8. 3 22
      kafka-ui-react-app/src/components/ConsumerGroups/ConsumerGroups.tsx
  9. 6 1
      kafka-ui-react-app/src/components/ConsumerGroups/Details/Details.tsx
  10. 6 2
      kafka-ui-react-app/src/components/ConsumerGroups/Details/ResetOffsets/ResetOffsets.tsx
  11. 2 17
      kafka-ui-react-app/src/components/KsqlDb/KsqlDb.tsx
  12. 7 1
      kafka-ui-react-app/src/components/KsqlDb/Query/Query.tsx
  13. 6 1
      kafka-ui-react-app/src/components/Schemas/Details/Details.tsx
  14. 117 101
      kafka-ui-react-app/src/components/Schemas/Diff/Diff.tsx
  15. 10 2
      kafka-ui-react-app/src/components/Schemas/Edit/Edit.tsx
  16. 10 2
      kafka-ui-react-app/src/components/Schemas/New/New.tsx
  17. 1 1
      kafka-ui-react-app/src/components/Schemas/New/__test__/New.spec.tsx
  18. 5 38
      kafka-ui-react-app/src/components/Schemas/Schemas.tsx
  19. 19 9
      kafka-ui-react-app/src/components/Topics/List/ActionsCell.tsx
  20. 1 1
      kafka-ui-react-app/src/components/Topics/List/ListPage.tsx
  21. 6 2
      kafka-ui-react-app/src/components/Topics/New/New.tsx
  22. 2 6
      kafka-ui-react-app/src/components/Topics/New/__test__/New.spec.tsx
  23. 0 0
      kafka-ui-react-app/src/components/Topics/Topic/ConsumerGroups/TopicConsumerGroups.tsx
  24. 1 1
      kafka-ui-react-app/src/components/Topics/Topic/ConsumerGroups/__test__/TopicConsumerGroups.spec.tsx
  25. 0 201
      kafka-ui-react-app/src/components/Topics/Topic/Details/Details.tsx
  26. 2 3
      kafka-ui-react-app/src/components/Topics/Topic/Edit/DangerZone/DangerZone.styled.tsx
  27. 0 2
      kafka-ui-react-app/src/components/Topics/Topic/Edit/Edit.tsx
  28. 1 5
      kafka-ui-react-app/src/components/Topics/Topic/Edit/__test__/Edit.spec.tsx
  29. 2 2
      kafka-ui-react-app/src/components/Topics/Topic/Messages/Filters/AddEditFilterContainer.tsx
  30. 4 4
      kafka-ui-react-app/src/components/Topics/Topic/Messages/Filters/AddFilter.tsx
  31. 2 2
      kafka-ui-react-app/src/components/Topics/Topic/Messages/Filters/EditFilter.tsx
  32. 4 4
      kafka-ui-react-app/src/components/Topics/Topic/Messages/Filters/FilterModal.tsx
  33. 2 6
      kafka-ui-react-app/src/components/Topics/Topic/Messages/Filters/Filters.styled.ts
  34. 2 2
      kafka-ui-react-app/src/components/Topics/Topic/Messages/Filters/Filters.tsx
  35. 0 0
      kafka-ui-react-app/src/components/Topics/Topic/Messages/Filters/FiltersContainer.ts
  36. 1 1
      kafka-ui-react-app/src/components/Topics/Topic/Messages/Filters/InfoModal.tsx
  37. 0 0
      kafka-ui-react-app/src/components/Topics/Topic/Messages/Filters/SavedFilters.tsx
  38. 2 2
      kafka-ui-react-app/src/components/Topics/Topic/Messages/Filters/__tests__/AddEditFilterContainer.spec.tsx
  39. 2 2
      kafka-ui-react-app/src/components/Topics/Topic/Messages/Filters/__tests__/AddFilter.spec.tsx
  40. 2 2
      kafka-ui-react-app/src/components/Topics/Topic/Messages/Filters/__tests__/EditFilter.spec.tsx
  41. 2 2
      kafka-ui-react-app/src/components/Topics/Topic/Messages/Filters/__tests__/FilterModal.spec.tsx
  42. 2 2
      kafka-ui-react-app/src/components/Topics/Topic/Messages/Filters/__tests__/Filters.spec.tsx
  43. 1 1
      kafka-ui-react-app/src/components/Topics/Topic/Messages/Filters/__tests__/Filters.styled.spec.tsx
  44. 1 1
      kafka-ui-react-app/src/components/Topics/Topic/Messages/Filters/__tests__/InfoModal.spec.tsx
  45. 2 2
      kafka-ui-react-app/src/components/Topics/Topic/Messages/Filters/__tests__/SavedFilters.spec.tsx
  46. 0 0
      kafka-ui-react-app/src/components/Topics/Topic/Messages/Filters/utils.ts
  47. 0 0
      kafka-ui-react-app/src/components/Topics/Topic/Messages/Message.tsx
  48. 0 0
      kafka-ui-react-app/src/components/Topics/Topic/Messages/MessageContent/MessageContent.styled.ts
  49. 0 0
      kafka-ui-react-app/src/components/Topics/Topic/Messages/MessageContent/MessageContent.tsx
  50. 1 1
      kafka-ui-react-app/src/components/Topics/Topic/Messages/MessageContent/__tests__/MessageContent.spec.tsx
  51. 0 0
      kafka-ui-react-app/src/components/Topics/Topic/Messages/Messages.tsx
  52. 0 0
      kafka-ui-react-app/src/components/Topics/Topic/Messages/MessagesTable.tsx
  53. 4 5
      kafka-ui-react-app/src/components/Topics/Topic/Messages/__test__/FiltersContainer.spec.tsx
  54. 2 4
      kafka-ui-react-app/src/components/Topics/Topic/Messages/__test__/Message.spec.tsx
  55. 1 1
      kafka-ui-react-app/src/components/Topics/Topic/Messages/__test__/Messages.spec.tsx
  56. 1 1
      kafka-ui-react-app/src/components/Topics/Topic/Messages/__test__/MessagesTable.spec.tsx
  57. 1 1
      kafka-ui-react-app/src/components/Topics/Topic/Messages/__test__/utils.spec.ts
  58. 0 0
      kafka-ui-react-app/src/components/Topics/Topic/Overview/Overview.styled.ts
  59. 0 0
      kafka-ui-react-app/src/components/Topics/Topic/Overview/Overview.tsx
  60. 2 2
      kafka-ui-react-app/src/components/Topics/Topic/Overview/__test__/Overview.spec.tsx
  61. 0 0
      kafka-ui-react-app/src/components/Topics/Topic/Settings/ConfigListItem.tsx
  62. 0 0
      kafka-ui-react-app/src/components/Topics/Topic/Settings/Settings.styled.ts
  63. 0 0
      kafka-ui-react-app/src/components/Topics/Topic/Settings/Settings.tsx
  64. 1 1
      kafka-ui-react-app/src/components/Topics/Topic/Settings/__test__/ConfigListItem.spec.tsx
  65. 6 10
      kafka-ui-react-app/src/components/Topics/Topic/Settings/__test__/Settings.spec.tsx
  66. 0 0
      kafka-ui-react-app/src/components/Topics/Topic/Statistics/Indicators/SizeStats.tsx
  67. 0 0
      kafka-ui-react-app/src/components/Topics/Topic/Statistics/Indicators/Total.tsx
  68. 0 0
      kafka-ui-react-app/src/components/Topics/Topic/Statistics/Metrics.tsx
  69. 0 0
      kafka-ui-react-app/src/components/Topics/Topic/Statistics/PartitionInfoRow.tsx
  70. 0 0
      kafka-ui-react-app/src/components/Topics/Topic/Statistics/PartitionTable.tsx
  71. 0 0
      kafka-ui-react-app/src/components/Topics/Topic/Statistics/Statistics.styles.ts
  72. 0 0
      kafka-ui-react-app/src/components/Topics/Topic/Statistics/Statistics.tsx
  73. 1 1
      kafka-ui-react-app/src/components/Topics/Topic/Statistics/__test__/Metrics.spec.tsx
  74. 1 1
      kafka-ui-react-app/src/components/Topics/Topic/Statistics/__test__/Statistics.spec.tsx
  75. 183 15
      kafka-ui-react-app/src/components/Topics/Topic/Topic.tsx
  76. 8 8
      kafka-ui-react-app/src/components/Topics/Topic/__test__/Topic.spec.tsx
  77. 0 69
      kafka-ui-react-app/src/components/Topics/Topic/__tests__/Topic.spec.tsx
  78. 4 33
      kafka-ui-react-app/src/components/Topics/Topics.tsx
  79. 6 6
      kafka-ui-react-app/src/components/Topics/shared/Form/TopicForm.tsx
  80. 0 16
      kafka-ui-react-app/src/components/common/Breadcrumb/Breadcrumb.context.ts
  81. 0 53
      kafka-ui-react-app/src/components/common/Breadcrumb/Breadcrumb.provider.tsx
  82. 0 52
      kafka-ui-react-app/src/components/common/Breadcrumb/Breadcrumb.route.tsx
  83. 0 15
      kafka-ui-react-app/src/components/common/Breadcrumb/Breadcrumb.styled.ts
  84. 0 49
      kafka-ui-react-app/src/components/common/Breadcrumb/Breadcrumb.tsx
  85. 0 40
      kafka-ui-react-app/src/components/common/Breadcrumb/__tests__/Breadcrumb.spec.tsx
  86. 1 1
      kafka-ui-react-app/src/components/common/ControlPanel/ControlPanel.styled.ts
  87. 7 1
      kafka-ui-react-app/src/components/common/Dropdown/Dropdown.styled.ts
  88. 2 2
      kafka-ui-react-app/src/components/common/Dropdown/Dropdown.tsx
  89. 1 2
      kafka-ui-react-app/src/components/common/Form/Form.styled.ts
  90. 42 0
      kafka-ui-react-app/src/components/common/PageHeading/PageHeading.styled.ts
  91. 17 19
      kafka-ui-react-app/src/components/common/PageHeading/PageHeading.tsx
  92. 0 5
      kafka-ui-react-app/src/lib/constants.ts
  93. 11 2
      kafka-ui-react-app/src/theme/theme.ts

+ 6 - 1
kafka-ui-react-app/src/components/Brokers/Broker/Broker.tsx

@@ -8,6 +8,7 @@ import {
   clusterBrokerMetricsRelativePath,
   clusterBrokerMetricsRelativePath,
   ClusterBrokerParam,
   ClusterBrokerParam,
   clusterBrokerPath,
   clusterBrokerPath,
+  clusterBrokersPath,
 } from 'lib/paths';
 } from 'lib/paths';
 import { useClusterStats } from 'lib/hooks/api/clusters';
 import { useClusterStats } from 'lib/hooks/api/clusters';
 import { useBrokers } from 'lib/hooks/api/brokers';
 import { useBrokers } from 'lib/hooks/api/brokers';
@@ -31,7 +32,11 @@ const Broker: React.FC = () => {
   );
   );
   return (
   return (
     <>
     <>
-      <PageHeading text={`Broker ${brokerId}`} />
+      <PageHeading
+        text={`Broker ${brokerId}`}
+        backTo={clusterBrokersPath(clusterName)}
+        backText="Brokers"
+      />
       <Metrics.Wrapper>
       <Metrics.Wrapper>
         <Metrics.Section>
         <Metrics.Section>
           <Metrics.Indicator label="Segment Size">
           <Metrics.Indicator label="Segment Size">

+ 2 - 17
kafka-ui-react-app/src/components/Brokers/Brokers.tsx

@@ -3,26 +3,11 @@ import { Route, Routes } from 'react-router-dom';
 import { getNonExactPath, RouteParams } from 'lib/paths';
 import { getNonExactPath, RouteParams } from 'lib/paths';
 import BrokersList from 'components/Brokers/BrokersList/BrokersList';
 import BrokersList from 'components/Brokers/BrokersList/BrokersList';
 import Broker from 'components/Brokers/Broker/Broker';
 import Broker from 'components/Brokers/Broker/Broker';
-import { BreadcrumbRoute } from 'components/common/Breadcrumb/Breadcrumb.route';
 
 
 const Brokers: React.FC = () => (
 const Brokers: React.FC = () => (
   <Routes>
   <Routes>
-    <Route
-      index
-      element={
-        <BreadcrumbRoute>
-          <BrokersList />
-        </BreadcrumbRoute>
-      }
-    />
-    <Route
-      path={getNonExactPath(RouteParams.brokerId)}
-      element={
-        <BreadcrumbRoute>
-          <Broker />
-        </BreadcrumbRoute>
-      }
-    />
+    <Route index element={<BrokersList />} />
+    <Route path={getNonExactPath(RouteParams.brokerId)} element={<Broker />} />
   </Routes>
   </Routes>
 );
 );
 
 

+ 1 - 1
kafka-ui-react-app/src/components/Brokers/BrokersList/BrokersList.tsx

@@ -60,7 +60,7 @@ const BrokersList: React.FC = () => {
 
 
   return (
   return (
     <>
     <>
-      <PageHeading text="Broker" />
+      <PageHeading text="Brokers" />
       <Metrics.Wrapper>
       <Metrics.Wrapper>
         <Metrics.Section title="Uptime">
         <Metrics.Section title="Uptime">
           <Metrics.Indicator label="Total Broker">
           <Metrics.Indicator label="Total Broker">

+ 39 - 73
kafka-ui-react-app/src/components/Cluster/Cluster.tsx

@@ -14,9 +14,6 @@ import {
   getNonExactPath,
   getNonExactPath,
 } from 'lib/paths';
 } from 'lib/paths';
 import ClusterContext from 'components/contexts/ClusterContext';
 import ClusterContext from 'components/contexts/ClusterContext';
-import Breadcrumb from 'components/common/Breadcrumb/Breadcrumb';
-import { BreadcrumbRoute } from 'components/common/Breadcrumb/Breadcrumb.route';
-import { BreadcrumbProvider } from 'components/common/Breadcrumb/Breadcrumb.provider';
 import PageLoader from 'components/common/PageLoader/PageLoader';
 import PageLoader from 'components/common/PageLoader/PageLoader';
 import { useClusters } from 'lib/hooks/api/clusters';
 import { useClusters } from 'lib/hooks/api/clusters';
 import Brokers from 'components/Brokers/Brokers';
 import Brokers from 'components/Brokers/Brokers';
@@ -60,84 +57,53 @@ const Cluster: React.FC = () => {
   }, [clusterName, data]);
   }, [clusterName, data]);
 
 
   return (
   return (
-    <BreadcrumbProvider>
-      <Breadcrumb />
-      <Suspense fallback={<PageLoader />}>
-        <ClusterContext.Provider value={contextValue}>
-          <Routes>
+    <Suspense fallback={<PageLoader />}>
+      <ClusterContext.Provider value={contextValue}>
+        <Routes>
+          <Route
+            path={getNonExactPath(clusterBrokerRelativePath)}
+            element={<Brokers />}
+          />
+          <Route
+            path={getNonExactPath(clusterTopicsRelativePath)}
+            element={<Topics />}
+          />
+          <Route
+            path={getNonExactPath(clusterConsumerGroupsRelativePath)}
+            element={<ConsumerGroups />}
+          />
+          {contextValue.hasSchemaRegistryConfigured && (
             <Route
             <Route
-              path={getNonExactPath(clusterBrokerRelativePath)}
-              element={
-                <BreadcrumbRoute>
-                  <Brokers />
-                </BreadcrumbRoute>
-              }
+              path={getNonExactPath(clusterSchemasRelativePath)}
+              element={<Schemas />}
             />
             />
+          )}
+          {contextValue.hasKafkaConnectConfigured && (
             <Route
             <Route
-              path={getNonExactPath(clusterTopicsRelativePath)}
-              element={
-                <BreadcrumbRoute>
-                  <Topics />
-                </BreadcrumbRoute>
-              }
+              path={getNonExactPath(clusterConnectsRelativePath)}
+              element={<Connect />}
             />
             />
+          )}
+          {contextValue.hasKafkaConnectConfigured && (
             <Route
             <Route
-              path={getNonExactPath(clusterConsumerGroupsRelativePath)}
-              element={
-                <BreadcrumbRoute>
-                  <ConsumerGroups />
-                </BreadcrumbRoute>
-              }
+              path={getNonExactPath(clusterConnectorsRelativePath)}
+              element={<Connect />}
             />
             />
-            {contextValue.hasSchemaRegistryConfigured && (
-              <Route
-                path={getNonExactPath(clusterSchemasRelativePath)}
-                element={
-                  <BreadcrumbRoute>
-                    <Schemas />
-                  </BreadcrumbRoute>
-                }
-              />
-            )}
-            {contextValue.hasKafkaConnectConfigured && (
-              <Route
-                path={getNonExactPath(clusterConnectsRelativePath)}
-                element={
-                  <BreadcrumbRoute>
-                    <Connect />
-                  </BreadcrumbRoute>
-                }
-              />
-            )}
-            {contextValue.hasKafkaConnectConfigured && (
-              <Route
-                path={getNonExactPath(clusterConnectorsRelativePath)}
-                element={
-                  <BreadcrumbRoute>
-                    <Connect />
-                  </BreadcrumbRoute>
-                }
-              />
-            )}
-            {contextValue.hasKsqlDbConfigured && (
-              <Route
-                path={getNonExactPath(clusterKsqlDbRelativePath)}
-                element={
-                  <BreadcrumbRoute>
-                    <KsqlDb />
-                  </BreadcrumbRoute>
-                }
-              />
-            )}
+          )}
+          {contextValue.hasKsqlDbConfigured && (
             <Route
             <Route
-              path="/"
-              element={<Navigate to={clusterBrokerRelativePath} replace />}
+              path={getNonExactPath(clusterKsqlDbRelativePath)}
+              element={<KsqlDb />}
             />
             />
-          </Routes>
-          <Outlet />
-        </ClusterContext.Provider>
-      </Suspense>
-    </BreadcrumbProvider>
+          )}
+          <Route
+            path="/"
+            element={<Navigate to={clusterBrokerRelativePath} replace />}
+          />
+        </Routes>
+        <Outlet />
+      </ClusterContext.Provider>
+    </Suspense>
   );
   );
 };
 };
 
 

+ 4 - 27
kafka-ui-react-app/src/components/Connect/Connect.tsx

@@ -9,7 +9,6 @@ import {
   getNonExactPath,
   getNonExactPath,
   clusterConnectorsPath,
   clusterConnectorsPath,
 } from 'lib/paths';
 } from 'lib/paths';
-import { BreadcrumbRoute } from 'components/common/Breadcrumb/Breadcrumb.route';
 import useAppParams from 'lib/hooks/useAppParams';
 import useAppParams from 'lib/hooks/useAppParams';
 
 
 import ListPage from './List/ListPage';
 import ListPage from './List/ListPage';
@@ -22,37 +21,15 @@ const Connect: React.FC = () => {
 
 
   return (
   return (
     <Routes>
     <Routes>
-      <Route
-        index
-        element={
-          <BreadcrumbRoute>
-            <ListPage />
-          </BreadcrumbRoute>
-        }
-      />
-      <Route
-        path={clusterConnectorNewRelativePath}
-        element={
-          <BreadcrumbRoute>
-            <New />
-          </BreadcrumbRoute>
-        }
-      />
+      <Route index element={<ListPage />} />
+      <Route path={clusterConnectorNewRelativePath} element={<New />} />
       <Route
       <Route
         path={clusterConnectConnectorEditRelativePath}
         path={clusterConnectConnectorEditRelativePath}
-        element={
-          <BreadcrumbRoute>
-            <Edit />
-          </BreadcrumbRoute>
-        }
+        element={<Edit />}
       />
       />
       <Route
       <Route
         path={getNonExactPath(clusterConnectConnectorRelativePath)}
         path={getNonExactPath(clusterConnectConnectorRelativePath)}
-        element={
-          <BreadcrumbRoute>
-            <DetailsPage />
-          </BreadcrumbRoute>
-        }
+        element={<DetailsPage />}
       />
       />
       <Route
       <Route
         path={clusterConnectConnectorsRelativePath}
         path={clusterConnectConnectorsRelativePath}

+ 6 - 1
kafka-ui-react-app/src/components/Connect/Details/DetailsPage.tsx

@@ -7,6 +7,7 @@ import {
   clusterConnectConnectorPath,
   clusterConnectConnectorPath,
   clusterConnectConnectorTasksPath,
   clusterConnectConnectorTasksPath,
   clusterConnectConnectorTasksRelativePath,
   clusterConnectConnectorTasksRelativePath,
+  clusterConnectorsPath,
   RouterParamsClusterConnectConnector,
   RouterParamsClusterConnectConnector,
 } from 'lib/paths';
 } from 'lib/paths';
 import Navbar from 'components/common/Navigation/Navbar.styled';
 import Navbar from 'components/common/Navigation/Navbar.styled';
@@ -24,7 +25,11 @@ const DetailsPage: React.FC = () => {
 
 
   return (
   return (
     <div>
     <div>
-      <PageHeading text={connectorName}>
+      <PageHeading
+        text={connectorName}
+        backTo={clusterConnectorsPath(clusterName)}
+        backText="Connectors"
+      >
         <Actions />
         <Actions />
       </PageHeading>
       </PageHeading>
       <Navbar role="navigation">
       <Navbar role="navigation">

+ 10 - 2
kafka-ui-react-app/src/components/Connect/New/New.tsx

@@ -4,7 +4,11 @@ import useAppParams from 'lib/hooks/useAppParams';
 import { Controller, FormProvider, useForm } from 'react-hook-form';
 import { Controller, FormProvider, useForm } from 'react-hook-form';
 import { ErrorMessage } from '@hookform/error-message';
 import { ErrorMessage } from '@hookform/error-message';
 import { yupResolver } from '@hookform/resolvers/yup';
 import { yupResolver } from '@hookform/resolvers/yup';
-import { clusterConnectConnectorPath, ClusterNameRoute } from 'lib/paths';
+import {
+  clusterConnectConnectorPath,
+  clusterConnectorsPath,
+  ClusterNameRoute,
+} from 'lib/paths';
 import yup from 'lib/yupExtended';
 import yup from 'lib/yupExtended';
 import Editor from 'components/common/Editor/Editor';
 import Editor from 'components/common/Editor/Editor';
 import Select from 'components/common/Select/Select';
 import Select from 'components/common/Select/Select';
@@ -91,7 +95,11 @@ const New: React.FC = () => {
 
 
   return (
   return (
     <FormProvider {...methods}>
     <FormProvider {...methods}>
-      <PageHeading text="Create new connector" />
+      <PageHeading
+        text="Create new connector"
+        backTo={clusterConnectorsPath(clusterName)}
+        backText="Connectors"
+      />
       <S.NewConnectFormStyled
       <S.NewConnectFormStyled
         onSubmit={handleSubmit(onSubmit)}
         onSubmit={handleSubmit(onSubmit)}
         aria-label="Create connect form"
         aria-label="Create connect form"

+ 3 - 22
kafka-ui-react-app/src/components/ConsumerGroups/ConsumerGroups.tsx

@@ -3,7 +3,6 @@ import { Route, Routes } from 'react-router-dom';
 import Details from 'components/ConsumerGroups/Details/Details';
 import Details from 'components/ConsumerGroups/Details/Details';
 import ListContainer from 'components/ConsumerGroups/List/ListContainer';
 import ListContainer from 'components/ConsumerGroups/List/ListContainer';
 import ResetOffsets from 'components/ConsumerGroups/Details/ResetOffsets/ResetOffsets';
 import ResetOffsets from 'components/ConsumerGroups/Details/ResetOffsets/ResetOffsets';
-import { BreadcrumbRoute } from 'components/common/Breadcrumb/Breadcrumb.route';
 import {
 import {
   clusterConsumerGroupResetOffsetsRelativePath,
   clusterConsumerGroupResetOffsetsRelativePath,
   RouteParams,
   RouteParams,
@@ -12,29 +11,11 @@ import {
 const ConsumerGroups: React.FC = () => {
 const ConsumerGroups: React.FC = () => {
   return (
   return (
     <Routes>
     <Routes>
-      <Route
-        index
-        element={
-          <BreadcrumbRoute>
-            <ListContainer />
-          </BreadcrumbRoute>
-        }
-      />
-      <Route
-        path={RouteParams.consumerGroupID}
-        element={
-          <BreadcrumbRoute>
-            <Details />
-          </BreadcrumbRoute>
-        }
-      />
+      <Route index element={<ListContainer />} />
+      <Route path={RouteParams.consumerGroupID} element={<Details />} />
       <Route
       <Route
         path={clusterConsumerGroupResetOffsetsRelativePath}
         path={clusterConsumerGroupResetOffsetsRelativePath}
-        element={
-          <BreadcrumbRoute>
-            <ResetOffsets />
-          </BreadcrumbRoute>
-        }
+        element={<ResetOffsets />}
       />
       />
     </Routes>
     </Routes>
   );
   );

+ 6 - 1
kafka-ui-react-app/src/components/ConsumerGroups/Details/Details.tsx

@@ -3,6 +3,7 @@ import { useNavigate } from 'react-router-dom';
 import useAppParams from 'lib/hooks/useAppParams';
 import useAppParams from 'lib/hooks/useAppParams';
 import {
 import {
   clusterConsumerGroupResetRelativePath,
   clusterConsumerGroupResetRelativePath,
+  clusterConsumerGroupsPath,
   ClusterGroupParam,
   ClusterGroupParam,
 } from 'lib/paths';
 } from 'lib/paths';
 import PageLoader from 'components/common/PageLoader/PageLoader';
 import PageLoader from 'components/common/PageLoader/PageLoader';
@@ -64,7 +65,11 @@ const Details: React.FC = () => {
   return (
   return (
     <div>
     <div>
       <div>
       <div>
-        <PageHeading text={consumerGroupID}>
+        <PageHeading
+          text={consumerGroupID}
+          backTo={clusterConsumerGroupsPath(clusterName)}
+          backText="Consumers"
+        >
           {!isReadOnly && (
           {!isReadOnly && (
             <Dropdown>
             <Dropdown>
               <DropdownItem onClick={onResetOffsets}>Reset offset</DropdownItem>
               <DropdownItem onClick={onResetOffsets}>Reset offset</DropdownItem>

+ 6 - 2
kafka-ui-react-app/src/components/ConsumerGroups/Details/ResetOffsets/ResetOffsets.tsx

@@ -1,7 +1,7 @@
 import React from 'react';
 import React from 'react';
 import { useNavigate } from 'react-router-dom';
 import { useNavigate } from 'react-router-dom';
 import { ConsumerGroupOffsetsResetType } from 'generated-sources';
 import { ConsumerGroupOffsetsResetType } from 'generated-sources';
-import { ClusterGroupParam } from 'lib/paths';
+import { clusterConsumerGroupsPath, ClusterGroupParam } from 'lib/paths';
 import {
 import {
   Controller,
   Controller,
   FormProvider,
   FormProvider,
@@ -170,7 +170,11 @@ const ResetOffsets: React.FC = () => {
 
 
   return (
   return (
     <FormProvider {...methods}>
     <FormProvider {...methods}>
-      <PageHeading text="Reset offsets" />
+      <PageHeading
+        text="Reset offsets"
+        backTo={clusterConsumerGroupsPath(clusterName)}
+        backText="Consumers"
+      />
       <S.Wrapper>
       <S.Wrapper>
         <form onSubmit={handleSubmit(onSubmit)}>
         <form onSubmit={handleSubmit(onSubmit)}>
           <S.MainSelectors>
           <S.MainSelectors>

+ 2 - 17
kafka-ui-react-app/src/components/KsqlDb/KsqlDb.tsx

@@ -3,27 +3,12 @@ import { Route, Routes } from 'react-router-dom';
 import { clusterKsqlDbQueryRelativePath } from 'lib/paths';
 import { clusterKsqlDbQueryRelativePath } from 'lib/paths';
 import List from 'components/KsqlDb/List/List';
 import List from 'components/KsqlDb/List/List';
 import Query from 'components/KsqlDb/Query/Query';
 import Query from 'components/KsqlDb/Query/Query';
-import { BreadcrumbRoute } from 'components/common/Breadcrumb/Breadcrumb.route';
 
 
 const KsqlDb: React.FC = () => {
 const KsqlDb: React.FC = () => {
   return (
   return (
     <Routes>
     <Routes>
-      <Route
-        path="/*"
-        element={
-          <BreadcrumbRoute>
-            <List />
-          </BreadcrumbRoute>
-        }
-      />
-      <Route
-        path={clusterKsqlDbQueryRelativePath}
-        element={
-          <BreadcrumbRoute>
-            <Query />
-          </BreadcrumbRoute>
-        }
-      />
+      <Route path="/*" element={<List />} />
+      <Route path={clusterKsqlDbQueryRelativePath} element={<Query />} />
     </Routes>
     </Routes>
   );
   );
 };
 };

+ 7 - 1
kafka-ui-react-app/src/components/KsqlDb/Query/Query.tsx

@@ -8,9 +8,10 @@ import {
 import { getKsqlExecution } from 'redux/reducers/ksqlDb/selectors';
 import { getKsqlExecution } from 'redux/reducers/ksqlDb/selectors';
 import { BASE_PARAMS } from 'lib/constants';
 import { BASE_PARAMS } from 'lib/constants';
 import { KsqlResponse, KsqlTableResponse } from 'generated-sources';
 import { KsqlResponse, KsqlTableResponse } from 'generated-sources';
-import { ClusterNameRoute } from 'lib/paths';
+import { clusterKsqlDbPath, ClusterNameRoute } from 'lib/paths';
 import { useAppDispatch, useAppSelector } from 'lib/hooks/redux';
 import { useAppDispatch, useAppSelector } from 'lib/hooks/redux';
 import { showAlert, showSuccessAlert } from 'lib/errorHandling';
 import { showAlert, showSuccessAlert } from 'lib/errorHandling';
+import PageHeading from 'components/common/PageHeading/PageHeading';
 
 
 import type { FormValues } from './QueryForm/QueryForm';
 import type { FormValues } from './QueryForm/QueryForm';
 import * as S from './Query.styled';
 import * as S from './Query.styled';
@@ -200,6 +201,11 @@ const Query: FC = () => {
 
 
   return (
   return (
     <>
     <>
+      <PageHeading
+        text="Query"
+        backText="KSQL DB"
+        backTo={clusterKsqlDbPath(clusterName)}
+      />
       <QueryForm
       <QueryForm
         fetching={fetching}
         fetching={fetching}
         hasResults={!!KSQLTable}
         hasResults={!!KSQLTable}

+ 6 - 1
kafka-ui-react-app/src/components/Schemas/Details/Details.tsx

@@ -4,6 +4,7 @@ import {
   ClusterSubjectParam,
   ClusterSubjectParam,
   clusterSchemaEditPageRelativePath,
   clusterSchemaEditPageRelativePath,
   clusterSchemaSchemaComparePageRelativePath,
   clusterSchemaSchemaComparePageRelativePath,
+  clusterSchemasPath,
 } from 'lib/paths';
 } from 'lib/paths';
 import ClusterContext from 'components/contexts/ClusterContext';
 import ClusterContext from 'components/contexts/ClusterContext';
 import PageLoader from 'components/common/PageLoader/PageLoader';
 import PageLoader from 'components/common/PageLoader/PageLoader';
@@ -74,7 +75,11 @@ const Details: React.FC = () => {
   }
   }
   return (
   return (
     <>
     <>
-      <PageHeading text={schema.subject}>
+      <PageHeading
+        text={schema.subject}
+        backText="Schema Registry"
+        backTo={clusterSchemasPath(clusterName)}
+      >
         {!isReadOnly && (
         {!isReadOnly && (
           <>
           <>
             <Button
             <Button

+ 117 - 101
kafka-ui-react-app/src/components/Schemas/Diff/Diff.tsx

@@ -1,6 +1,10 @@
 import React from 'react';
 import React from 'react';
 import { SchemaSubject } from 'generated-sources';
 import { SchemaSubject } from 'generated-sources';
-import { clusterSchemaComparePath, ClusterSubjectParam } from 'lib/paths';
+import {
+  clusterSchemaComparePath,
+  clusterSchemasPath,
+  ClusterSubjectParam,
+} from 'lib/paths';
 import PageLoader from 'components/common/PageLoader/PageLoader';
 import PageLoader from 'components/common/PageLoader/PageLoader';
 import DiffViewer from 'components/common/DiffViewer/DiffViewer';
 import DiffViewer from 'components/common/DiffViewer/DiffViewer';
 import { useNavigate, useLocation } from 'react-router-dom';
 import { useNavigate, useLocation } from 'react-router-dom';
@@ -13,6 +17,7 @@ import Select from 'components/common/Select/Select';
 import { useAppDispatch } from 'lib/hooks/redux';
 import { useAppDispatch } from 'lib/hooks/redux';
 import { resetLoaderById } from 'redux/reducers/loader/loaderSlice';
 import { resetLoaderById } from 'redux/reducers/loader/loaderSlice';
 import useAppParams from 'lib/hooks/useAppParams';
 import useAppParams from 'lib/hooks/useAppParams';
+import PageHeading from 'components/common/PageHeading/PageHeading';
 
 
 import * as S from './Diff.styled';
 import * as S from './Diff.styled';
 
 
@@ -66,109 +71,120 @@ const Diff: React.FC<DiffProps> = ({ versions, areVersionsFetched }) => {
   } = methods;
   } = methods;
 
 
   return (
   return (
-    <S.Section>
-      {areVersionsFetched ? (
-        <S.DiffBox>
-          <S.DiffTilesWrapper>
-            <S.DiffTile>
-              <S.DiffVersionsSelect>
-                <Controller
-                  defaultValue={leftVersion}
-                  control={control}
-                  rules={{ required: true }}
-                  name="schemaType"
-                  render={({ field: { name } }) => (
-                    <Select
-                      id="left-select"
-                      name={name}
-                      value={
-                        leftVersion === '' ? versions[0].version : leftVersion
-                      }
-                      onChange={(event) => {
-                        navigate(
-                          clusterSchemaComparePath(clusterName, subject)
-                        );
-                        searchParams.set('leftVersion', event.toString());
-                        searchParams.set(
-                          'rightVersion',
+    <>
+      <PageHeading
+        text={`${subject} compare versions`}
+        backText="Schema Registry"
+        backTo={clusterSchemasPath(clusterName)}
+      />
+      <S.Section>
+        {areVersionsFetched ? (
+          <S.DiffBox>
+            <S.DiffTilesWrapper>
+              <S.DiffTile>
+                <S.DiffVersionsSelect>
+                  <Controller
+                    defaultValue={leftVersion}
+                    control={control}
+                    rules={{ required: true }}
+                    name="schemaType"
+                    render={({ field: { name } }) => (
+                      <Select
+                        id="left-select"
+                        name={name}
+                        value={
+                          leftVersion === '' ? versions[0].version : leftVersion
+                        }
+                        onChange={(event) => {
+                          navigate(
+                            clusterSchemaComparePath(clusterName, subject)
+                          );
+                          searchParams.set('leftVersion', event.toString());
+                          searchParams.set(
+                            'rightVersion',
+                            rightVersion === ''
+                              ? versions[0].version
+                              : rightVersion
+                          );
+                          navigate({
+                            search: `?${searchParams.toString()}`,
+                          });
+                          setLeftVersion(event.toString());
+                        }}
+                        minWidth="100%"
+                        disabled={isSubmitting}
+                        options={versions.map((type) => ({
+                          value: type.version,
+                          label: `Version ${type.version}`,
+                        }))}
+                      />
+                    )}
+                  />
+                </S.DiffVersionsSelect>
+              </S.DiffTile>
+              <S.DiffTile>
+                <S.DiffVersionsSelect>
+                  <Controller
+                    defaultValue={rightVersion}
+                    control={control}
+                    rules={{ required: true }}
+                    name="schemaType"
+                    render={({ field: { name } }) => (
+                      <Select
+                        id="right-select"
+                        name={name}
+                        value={
                           rightVersion === ''
                           rightVersion === ''
                             ? versions[0].version
                             ? versions[0].version
                             : rightVersion
                             : rightVersion
-                        );
-                        navigate({
-                          search: `?${searchParams.toString()}`,
-                        });
-                        setLeftVersion(event.toString());
-                      }}
-                      minWidth="100%"
-                      disabled={isSubmitting}
-                      options={versions.map((type) => ({
-                        value: type.version,
-                        label: `Version ${type.version}`,
-                      }))}
-                    />
-                  )}
-                />
-              </S.DiffVersionsSelect>
-            </S.DiffTile>
-            <S.DiffTile>
-              <S.DiffVersionsSelect>
-                <Controller
-                  defaultValue={rightVersion}
-                  control={control}
-                  rules={{ required: true }}
-                  name="schemaType"
-                  render={({ field: { name } }) => (
-                    <Select
-                      id="right-select"
-                      name={name}
-                      value={
-                        rightVersion === '' ? versions[0].version : rightVersion
-                      }
-                      onChange={(event) => {
-                        navigate(
-                          clusterSchemaComparePath(clusterName, subject)
-                        );
-                        searchParams.set(
-                          'leftVersion',
-                          leftVersion === '' ? versions[0].version : leftVersion
-                        );
-                        searchParams.set('rightVersion', event.toString());
-                        navigate({
-                          search: `?${searchParams.toString()}`,
-                        });
-                        setRightVersion(event.toString());
-                      }}
-                      minWidth="100%"
-                      disabled={isSubmitting}
-                      options={versions.map((type) => ({
-                        value: type.version,
-                        label: `Version ${type.version}`,
-                      }))}
-                    />
-                  )}
-                />
-              </S.DiffVersionsSelect>
-            </S.DiffTile>
-          </S.DiffTilesWrapper>
-          <S.DiffWrapper>
-            <DiffViewer
-              value={[
-                getSchemaContent(versions, leftVersion),
-                getSchemaContent(versions, rightVersion),
-              ]}
-              setOptions={{
-                autoScrollEditorIntoView: true,
-              }}
-              isFixedHeight={false}
-              schemaType={getSchemaType(versions)}
-            />
-          </S.DiffWrapper>
-        </S.DiffBox>
-      ) : (
-        <PageLoader />
-      )}
-    </S.Section>
+                        }
+                        onChange={(event) => {
+                          navigate(
+                            clusterSchemaComparePath(clusterName, subject)
+                          );
+                          searchParams.set(
+                            'leftVersion',
+                            leftVersion === ''
+                              ? versions[0].version
+                              : leftVersion
+                          );
+                          searchParams.set('rightVersion', event.toString());
+                          navigate({
+                            search: `?${searchParams.toString()}`,
+                          });
+                          setRightVersion(event.toString());
+                        }}
+                        minWidth="100%"
+                        disabled={isSubmitting}
+                        options={versions.map((type) => ({
+                          value: type.version,
+                          label: `Version ${type.version}`,
+                        }))}
+                      />
+                    )}
+                  />
+                </S.DiffVersionsSelect>
+              </S.DiffTile>
+            </S.DiffTilesWrapper>
+            <S.DiffWrapper>
+              <DiffViewer
+                value={[
+                  getSchemaContent(versions, leftVersion),
+                  getSchemaContent(versions, rightVersion),
+                ]}
+                setOptions={{
+                  autoScrollEditorIntoView: true,
+                }}
+                isFixedHeight={false}
+                schemaType={getSchemaType(versions)}
+              />
+            </S.DiffWrapper>
+          </S.DiffBox>
+        ) : (
+          <PageLoader />
+        )}
+      </S.Section>
+    </>
   );
   );
 };
 };
 
 

+ 10 - 2
kafka-ui-react-app/src/components/Schemas/Edit/Edit.tsx

@@ -5,7 +5,11 @@ import {
   CompatibilityLevelCompatibilityEnum,
   CompatibilityLevelCompatibilityEnum,
   SchemaType,
   SchemaType,
 } from 'generated-sources';
 } from 'generated-sources';
-import { clusterSchemaPath, ClusterSubjectParam } from 'lib/paths';
+import {
+  clusterSchemaPath,
+  clusterSchemasPath,
+  ClusterSubjectParam,
+} from 'lib/paths';
 import { NewSchemaSubjectRaw } from 'redux/interfaces';
 import { NewSchemaSubjectRaw } from 'redux/interfaces';
 import Editor from 'components/common/Editor/Editor';
 import Editor from 'components/common/Editor/Editor';
 import Select from 'components/common/Select/Select';
 import Select from 'components/common/Select/Select';
@@ -99,7 +103,11 @@ const Edit: React.FC = () => {
   }
   }
   return (
   return (
     <FormProvider {...methods}>
     <FormProvider {...methods}>
-      <PageHeading text="Edit schema" />
+      <PageHeading
+        text="Edit"
+        backText="Schema Registry"
+        backTo={clusterSchemasPath(clusterName)}
+      />
       <S.EditWrapper>
       <S.EditWrapper>
         <form onSubmit={handleSubmit(onSubmit)}>
         <form onSubmit={handleSubmit(onSubmit)}>
           <div>
           <div>

+ 10 - 2
kafka-ui-react-app/src/components/Schemas/New/New.tsx

@@ -2,7 +2,11 @@ import React from 'react';
 import { NewSchemaSubjectRaw } from 'redux/interfaces';
 import { NewSchemaSubjectRaw } from 'redux/interfaces';
 import { FormProvider, useForm, Controller } from 'react-hook-form';
 import { FormProvider, useForm, Controller } from 'react-hook-form';
 import { ErrorMessage } from '@hookform/error-message';
 import { ErrorMessage } from '@hookform/error-message';
-import { ClusterNameRoute, clusterSchemaPath } from 'lib/paths';
+import {
+  ClusterNameRoute,
+  clusterSchemaPath,
+  clusterSchemasPath,
+} from 'lib/paths';
 import { SchemaType } from 'generated-sources';
 import { SchemaType } from 'generated-sources';
 import { SCHEMA_NAME_VALIDATION_PATTERN } from 'lib/constants';
 import { SCHEMA_NAME_VALIDATION_PATTERN } from 'lib/constants';
 import { useNavigate } from 'react-router-dom';
 import { useNavigate } from 'react-router-dom';
@@ -63,7 +67,11 @@ const New: React.FC = () => {
 
 
   return (
   return (
     <FormProvider {...methods}>
     <FormProvider {...methods}>
-      <PageHeading text="Create new schema" />
+      <PageHeading
+        text="Create"
+        backText="Schema Registry"
+        backTo={clusterSchemasPath(clusterName)}
+      />
       <S.Form onSubmit={handleSubmit(onSubmit)}>
       <S.Form onSubmit={handleSubmit(onSubmit)}>
         <div>
         <div>
           <InputLabel>Subject *</InputLabel>
           <InputLabel>Subject *</InputLabel>

+ 1 - 1
kafka-ui-react-app/src/components/Schemas/New/__test__/New.spec.tsx

@@ -22,7 +22,7 @@ describe('New Component', () => {
   });
   });
 
 
   it('renders component', () => {
   it('renders component', () => {
-    expect(screen.getByText('Create new schema')).toBeInTheDocument();
+    expect(screen.getByText('Create')).toBeInTheDocument();
   });
   });
   it('submit button will be disabled while form fields are not filled', () => {
   it('submit button will be disabled while form fields are not filled', () => {
     const submitBtn = screen.getByRole('button', { name: /submit/i });
     const submitBtn = screen.getByRole('button', { name: /submit/i });

+ 5 - 38
kafka-ui-react-app/src/components/Schemas/Schemas.tsx

@@ -11,50 +11,17 @@ import Details from 'components/Schemas/Details/Details';
 import New from 'components/Schemas/New/New';
 import New from 'components/Schemas/New/New';
 import Edit from 'components/Schemas/Edit/Edit';
 import Edit from 'components/Schemas/Edit/Edit';
 import DiffContainer from 'components/Schemas/Diff/DiffContainer';
 import DiffContainer from 'components/Schemas/Diff/DiffContainer';
-import { BreadcrumbRoute } from 'components/common/Breadcrumb/Breadcrumb.route';
 
 
 const Schemas: React.FC = () => {
 const Schemas: React.FC = () => {
   return (
   return (
     <Routes>
     <Routes>
-      <Route
-        index
-        element={
-          <BreadcrumbRoute>
-            <List />
-          </BreadcrumbRoute>
-        }
-      />
-      <Route
-        path={clusterSchemaNewRelativePath}
-        element={
-          <BreadcrumbRoute>
-            <New />
-          </BreadcrumbRoute>
-        }
-      />
-      <Route
-        path={RouteParams.subject}
-        element={
-          <BreadcrumbRoute>
-            <Details />
-          </BreadcrumbRoute>
-        }
-      />
-      <Route
-        path={clusterSchemaEditRelativePath}
-        element={
-          <BreadcrumbRoute>
-            <Edit />
-          </BreadcrumbRoute>
-        }
-      />
+      <Route index element={<List />} />
+      <Route path={clusterSchemaNewRelativePath} element={<New />} />
+      <Route path={RouteParams.subject} element={<Details />} />
+      <Route path={clusterSchemaEditRelativePath} element={<Edit />} />
       <Route
       <Route
         path={clusterSchemaSchemaDiffRelativePath}
         path={clusterSchemaSchemaDiffRelativePath}
-        element={
-          <BreadcrumbRoute>
-            <DiffContainer />
-          </BreadcrumbRoute>
-        }
+        element={<DiffContainer />}
       />
       />
     </Routes>
     </Routes>
   );
   );

+ 19 - 9
kafka-ui-react-app/src/components/Topics/List/ActionsCell.tsx

@@ -6,7 +6,11 @@ import ClusterContext from 'components/contexts/ClusterContext';
 import { ClusterNameRoute } from 'lib/paths';
 import { ClusterNameRoute } from 'lib/paths';
 import useAppParams from 'lib/hooks/useAppParams';
 import useAppParams from 'lib/hooks/useAppParams';
 import { clearTopicMessages } from 'redux/reducers/topicMessages/topicMessagesSlice';
 import { clearTopicMessages } from 'redux/reducers/topicMessages/topicMessagesSlice';
-import { Dropdown, DropdownItem } from 'components/common/Dropdown';
+import {
+  Dropdown,
+  DropdownItem,
+  DropdownItemHint,
+} from 'components/common/Dropdown';
 import { useQueryClient } from '@tanstack/react-query';
 import { useQueryClient } from '@tanstack/react-query';
 import {
 import {
   topicKeys,
   topicKeys,
@@ -26,7 +30,7 @@ const ActionsCell: React.FC<CellContext<Topic, unknown>> = ({ row }) => {
   const deleteTopic = useDeleteTopic(clusterName);
   const deleteTopic = useDeleteTopic(clusterName);
   const recreateTopic = useRecreateTopic({ clusterName, topicName: name });
   const recreateTopic = useRecreateTopic({ clusterName, topicName: name });
 
 
-  const isHidden = internal || isReadOnly;
+  const disabled = internal || isReadOnly;
 
 
   const clearTopicMessagesHandler = async () => {
   const clearTopicMessagesHandler = async () => {
     await dispatch(
     await dispatch(
@@ -38,15 +42,19 @@ const ActionsCell: React.FC<CellContext<Topic, unknown>> = ({ row }) => {
   const isCleanupDisabled = cleanUpPolicy !== CleanUpPolicy.DELETE;
   const isCleanupDisabled = cleanUpPolicy !== CleanUpPolicy.DELETE;
 
 
   return (
   return (
-    <Dropdown disabled={isHidden}>
+    <Dropdown disabled={disabled}>
       <DropdownItem
       <DropdownItem
         disabled={isCleanupDisabled}
         disabled={isCleanupDisabled}
         onClick={clearTopicMessagesHandler}
         onClick={clearTopicMessagesHandler}
         confirm="Are you sure want to clear topic messages?"
         confirm="Are you sure want to clear topic messages?"
         danger
         danger
-        title="Cleanup is alowed only for topics with DELETE policy"
       >
       >
         Clear Messages
         Clear Messages
+        <DropdownItemHint>
+          Clearing messages is only allowed for topics
+          <br />
+          with DELETE policy
+        </DropdownItemHint>
       </DropdownItem>
       </DropdownItem>
       <DropdownItem
       <DropdownItem
         onClick={recreateTopic.mutateAsync}
         onClick={recreateTopic.mutateAsync}
@@ -67,14 +75,16 @@ const ActionsCell: React.FC<CellContext<Topic, unknown>> = ({ row }) => {
             Are you sure want to remove <b>{name}</b> topic?
             Are you sure want to remove <b>{name}</b> topic?
           </>
           </>
         }
         }
-        title={
-          isTopicDeletionAllowed
-            ? 'The topic deletion is restricted by app configuration'
-            : ''
-        }
         danger
         danger
       >
       >
         Remove Topic
         Remove Topic
+        {!isTopicDeletionAllowed && (
+          <DropdownItemHint>
+            The topic deletion is restricted at the application
+            <br />
+            configuration level
+          </DropdownItemHint>
+        )}
       </DropdownItem>
       </DropdownItem>
     </Dropdown>
     </Dropdown>
   );
   );

+ 1 - 1
kafka-ui-react-app/src/components/Topics/List/ListPage.tsx

@@ -47,7 +47,7 @@ const ListPage: React.FC = () => {
 
 
   return (
   return (
     <>
     <>
-      <PageHeading text="All Topics">
+      <PageHeading text="Topics">
         {!isReadOnly && (
         {!isReadOnly && (
           <Button
           <Button
             buttonType="primary"
             buttonType="primary"

+ 6 - 2
kafka-ui-react-app/src/components/Topics/New/New.tsx

@@ -1,7 +1,7 @@
 import React from 'react';
 import React from 'react';
 import { TopicFormData } from 'redux/interfaces';
 import { TopicFormData } from 'redux/interfaces';
 import { useForm, FormProvider } from 'react-hook-form';
 import { useForm, FormProvider } from 'react-hook-form';
-import { ClusterNameRoute } from 'lib/paths';
+import { ClusterNameRoute, clusterTopicsPath } from 'lib/paths';
 import TopicForm from 'components/Topics/shared/Form/TopicForm';
 import TopicForm from 'components/Topics/shared/Form/TopicForm';
 import { useNavigate, useLocation } from 'react-router-dom';
 import { useNavigate, useLocation } from 'react-router-dom';
 import { yupResolver } from '@hookform/resolvers/yup';
 import { yupResolver } from '@hookform/resolvers/yup';
@@ -45,7 +45,11 @@ const New: React.FC = () => {
 
 
   return (
   return (
     <>
     <>
-      <PageHeading text={search ? 'Copy Topic' : 'Create new Topic'} />
+      <PageHeading
+        text={search ? 'Copy' : 'Create'}
+        backText="Topics"
+        backTo={clusterTopicsPath(clusterName)}
+      />
       <FormProvider {...methods}>
       <FormProvider {...methods}>
         <TopicForm
         <TopicForm
           topicName={name}
           topicName={name}

+ 2 - 6
kafka-ui-react-app/src/components/Topics/New/__test__/New.spec.tsx

@@ -48,18 +48,14 @@ describe('New', () => {
 
 
   it('checks header for create new', async () => {
   it('checks header for create new', async () => {
     await act(() => renderComponent(clusterTopicNewPath(clusterName)));
     await act(() => renderComponent(clusterTopicNewPath(clusterName)));
-    expect(
-      screen.getByRole('heading', { name: 'Create new Topic' })
-    ).toHaveTextContent('Create new Topic');
+    expect(screen.getByRole('heading', { name: 'Create' })).toBeInTheDocument();
   });
   });
 
 
   it('checks header for copy', async () => {
   it('checks header for copy', async () => {
     await act(() =>
     await act(() =>
       renderComponent(`${clusterTopicCopyPath(clusterName)}?name=test`)
       renderComponent(`${clusterTopicCopyPath(clusterName)}?name=test`)
     );
     );
-    expect(
-      screen.getByRole('heading', { name: 'Copy Topic' })
-    ).toHaveTextContent('Copy Topic');
+    expect(screen.getByRole('heading', { name: 'Copy' })).toBeInTheDocument();
   });
   });
 
 
   it('validates form', async () => {
   it('validates form', async () => {

+ 0 - 0
kafka-ui-react-app/src/components/Topics/Topic/Details/ConsumerGroups/TopicConsumerGroups.tsx → kafka-ui-react-app/src/components/Topics/Topic/ConsumerGroups/TopicConsumerGroups.tsx


+ 1 - 1
kafka-ui-react-app/src/components/Topics/Topic/Details/ConsumerGroups/__test__/TopicConsumerGroups.spec.tsx → kafka-ui-react-app/src/components/Topics/Topic/ConsumerGroups/__test__/TopicConsumerGroups.spec.tsx

@@ -1,7 +1,7 @@
 import React from 'react';
 import React from 'react';
 import { render, WithRoute } from 'lib/testHelpers';
 import { render, WithRoute } from 'lib/testHelpers';
 import { screen } from '@testing-library/react';
 import { screen } from '@testing-library/react';
-import TopicConsumerGroups from 'components/Topics/Topic/Details/ConsumerGroups/TopicConsumerGroups';
+import TopicConsumerGroups from 'components/Topics/Topic/ConsumerGroups/TopicConsumerGroups';
 import { clusterTopicConsumerGroupsPath } from 'lib/paths';
 import { clusterTopicConsumerGroupsPath } from 'lib/paths';
 import { useTopicConsumerGroups } from 'lib/hooks/api/topics';
 import { useTopicConsumerGroups } from 'lib/hooks/api/topics';
 import { ConsumerGroup } from 'generated-sources';
 import { ConsumerGroup } from 'generated-sources';

+ 0 - 201
kafka-ui-react-app/src/components/Topics/Topic/Details/Details.tsx

@@ -1,201 +0,0 @@
-import React, { Suspense } from 'react';
-import { NavLink, Route, Routes, useNavigate } from 'react-router-dom';
-import {
-  RouteParamsClusterTopic,
-  clusterTopicMessagesRelativePath,
-  clusterTopicSettingsRelativePath,
-  clusterTopicConsumerGroupsRelativePath,
-  clusterTopicEditRelativePath,
-  clusterTopicSendMessageRelativePath,
-  clusterTopicStatisticsRelativePath,
-} from 'lib/paths';
-import ClusterContext from 'components/contexts/ClusterContext';
-import PageHeading from 'components/common/PageHeading/PageHeading';
-import { Button } from 'components/common/Button/Button';
-import styled from 'styled-components';
-import Navbar from 'components/common/Navigation/Navbar.styled';
-import { useAppDispatch } from 'lib/hooks/redux';
-import useAppParams from 'lib/hooks/useAppParams';
-import {
-  Dropdown,
-  DropdownItem,
-  DropdownItemHint,
-} from 'components/common/Dropdown';
-import {
-  useDeleteTopic,
-  useRecreateTopic,
-  useTopicDetails,
-} from 'lib/hooks/api/topics';
-import { clearTopicMessages } from 'redux/reducers/topicMessages/topicMessagesSlice';
-import { CleanUpPolicy } from 'generated-sources';
-import PageLoader from 'components/common/PageLoader/PageLoader';
-
-import Messages from './Messages/Messages';
-import Overview from './Overview/Overview';
-import Settings from './Settings/Settings';
-import TopicConsumerGroups from './ConsumerGroups/TopicConsumerGroups';
-import Statistics from './Statistics/Statistics';
-
-const HeaderControlsWrapper = styled.div`
-  display: flex;
-  justify-content: flex-end;
-  align-self: center;
-  gap: 26px;
-`;
-
-const Details: React.FC = () => {
-  const dispatch = useAppDispatch();
-  const { clusterName, topicName } = useAppParams<RouteParamsClusterTopic>();
-  const navigate = useNavigate();
-  const deleteTopic = useDeleteTopic(clusterName);
-  const recreateTopic = useRecreateTopic({ clusterName, topicName });
-  const { data } = useTopicDetails({ clusterName, topicName });
-
-  const { isReadOnly, isTopicDeletionAllowed } =
-    React.useContext(ClusterContext);
-
-  const deleteTopicHandler = async () => {
-    await deleteTopic.mutateAsync(topicName);
-    navigate('../..');
-  };
-
-  return (
-    <div>
-      <PageHeading text={topicName}>
-        <HeaderControlsWrapper>
-          <Routes>
-            <Route
-              path={clusterTopicMessagesRelativePath}
-              element={
-                <Button
-                  buttonSize="M"
-                  buttonType="primary"
-                  to={`../${clusterTopicSendMessageRelativePath}`}
-                  disabled={isReadOnly}
-                >
-                  Produce Message
-                </Button>
-              }
-            />
-          </Routes>
-          {!isReadOnly && !data?.internal && (
-            <Routes>
-              <Route
-                index
-                element={
-                  <Dropdown>
-                    <DropdownItem
-                      onClick={() => navigate(clusterTopicEditRelativePath)}
-                    >
-                      Edit settings
-                      <DropdownItemHint>
-                        Pay attention! This operation has
-                        <br />
-                        especially important consequences.
-                      </DropdownItemHint>
-                    </DropdownItem>
-                    {data?.cleanUpPolicy === CleanUpPolicy.DELETE && (
-                      <DropdownItem
-                        onClick={() =>
-                          dispatch(
-                            clearTopicMessages({ clusterName, topicName })
-                          ).unwrap()
-                        }
-                        confirm="Are you sure want to clear topic messages?"
-                        danger
-                      >
-                        Clear messages
-                      </DropdownItem>
-                    )}
-                    <DropdownItem
-                      onClick={recreateTopic.mutateAsync}
-                      confirm={
-                        <>
-                          Are you sure want to recreate <b>{topicName}</b>{' '}
-                          topic?
-                        </>
-                      }
-                      danger
-                    >
-                      Recreate Topic
-                    </DropdownItem>
-                    {isTopicDeletionAllowed && (
-                      <DropdownItem
-                        onClick={deleteTopicHandler}
-                        confirm={
-                          <>
-                            Are you sure want to remove <b>{topicName}</b>{' '}
-                            topic?
-                          </>
-                        }
-                        danger
-                      >
-                        Remove Topic
-                      </DropdownItem>
-                    )}
-                  </Dropdown>
-                }
-              />
-            </Routes>
-          )}
-        </HeaderControlsWrapper>
-      </PageHeading>
-      <Navbar role="navigation">
-        <NavLink
-          to="."
-          className={({ isActive }) => (isActive ? 'is-active' : '')}
-          end
-        >
-          Overview
-        </NavLink>
-        <NavLink
-          to={clusterTopicMessagesRelativePath}
-          className={({ isActive }) => (isActive ? 'is-active' : '')}
-        >
-          Messages
-        </NavLink>
-        <NavLink
-          to={clusterTopicConsumerGroupsRelativePath}
-          className={({ isActive }) => (isActive ? 'is-active' : '')}
-        >
-          Consumers
-        </NavLink>
-        <NavLink
-          to={clusterTopicSettingsRelativePath}
-          className={({ isActive }) => (isActive ? 'is-active' : '')}
-        >
-          Settings
-        </NavLink>
-        <NavLink
-          to={clusterTopicStatisticsRelativePath}
-          className={({ isActive }) => (isActive ? 'is-active' : '')}
-        >
-          Statistics
-        </NavLink>
-      </Navbar>
-      <Suspense fallback={<PageLoader />}>
-        <Routes>
-          <Route index element={<Overview />} />
-          <Route
-            path={clusterTopicMessagesRelativePath}
-            element={<Messages />}
-          />
-          <Route
-            path={clusterTopicSettingsRelativePath}
-            element={<Settings />}
-          />
-          <Route
-            path={clusterTopicConsumerGroupsRelativePath}
-            element={<TopicConsumerGroups />}
-          />
-          <Route
-            path={clusterTopicStatisticsRelativePath}
-            element={<Statistics />}
-          />
-        </Routes>
-      </Suspense>
-    </div>
-  );
-};
-
-export default Details;

+ 2 - 3
kafka-ui-react-app/src/components/Topics/Topic/Edit/DangerZone/DangerZone.styled.tsx

@@ -1,11 +1,10 @@
 import styled from 'styled-components';
 import styled from 'styled-components';
 
 
 export const Wrapper = styled.div`
 export const Wrapper = styled.div`
-  margin: 32px auto;
+  margin-top: 16px;
   padding: 16px;
   padding: 16px;
-  border: 1px solid ${({ theme }) => theme.dangerZone.borderColor};
+  border-top: 1px solid ${({ theme }) => theme.dangerZone.borderColor};
   box-sizing: border-box;
   box-sizing: border-box;
-  border-radius: 8px;
   width: 768px;
   width: 768px;
 
 
   & > div {
   & > div {

+ 0 - 2
kafka-ui-react-app/src/components/Topics/Topic/Edit/Edit.tsx

@@ -10,7 +10,6 @@ import { RouteParamsClusterTopic } from 'lib/paths';
 import { useNavigate } from 'react-router-dom';
 import { useNavigate } from 'react-router-dom';
 import { yupResolver } from '@hookform/resolvers/yup';
 import { yupResolver } from '@hookform/resolvers/yup';
 import { topicFormValidationSchema } from 'lib/yupExtended';
 import { topicFormValidationSchema } from 'lib/yupExtended';
-import PageHeading from 'components/common/PageHeading/PageHeading';
 import useAppParams from 'lib/hooks/useAppParams';
 import useAppParams from 'lib/hooks/useAppParams';
 import topicParamsTransformer from 'components/Topics/Topic/Edit/topicParamsTransformer';
 import topicParamsTransformer from 'components/Topics/Topic/Edit/topicParamsTransformer';
 import { MILLISECONDS_IN_WEEK } from 'lib/constants';
 import { MILLISECONDS_IN_WEEK } from 'lib/constants';
@@ -63,7 +62,6 @@ const Edit: React.FC = () => {
 
 
   return (
   return (
     <>
     <>
-      <PageHeading text={`Edit ${topicName}`} />
       <FormProvider {...methods}>
       <FormProvider {...methods}>
         <TopicForm
         <TopicForm
           topicName={topicName}
           topicName={topicName}

+ 1 - 5
kafka-ui-react-app/src/components/Topics/Topic/Edit/__test__/Edit.spec.tsx

@@ -62,17 +62,13 @@ describe('Edit Component', () => {
     await act(() => renderComponent());
     await act(() => renderComponent());
   });
   });
 
 
-  it('renders component', async () => {
-    expect(screen.getByText(`Edit ${topicName}`)).toBeInTheDocument();
-  });
-
   it('renders DangerZone component', async () => {
   it('renders DangerZone component', async () => {
     expect(screen.getByText(`DangerZone`)).toBeInTheDocument();
     expect(screen.getByText(`DangerZone`)).toBeInTheDocument();
   });
   });
 
 
   it('submits form correctly', async () => {
   it('submits form correctly', async () => {
     await act(() => renderComponent());
     await act(() => renderComponent());
-    const btn = screen.getAllByText(/Save/i)[0];
+    const btn = screen.getAllByText(/Update topic/i)[0];
     const field = screen.getByRole('spinbutton', {
     const field = screen.getByRole('spinbutton', {
       name: 'Min In Sync Replicas * Min In Sync Replicas *',
       name: 'Min In Sync Replicas * Min In Sync Replicas *',
     });
     });

+ 2 - 2
kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/Filters/AddEditFilterContainer.tsx → kafka-ui-react-app/src/components/Topics/Topic/Messages/Filters/AddEditFilterContainer.tsx

@@ -1,12 +1,12 @@
 import React from 'react';
 import React from 'react';
-import * as S from 'components/Topics/Topic/Details/Messages/Filters/Filters.styled';
+import * as S from 'components/Topics/Topic/Messages/Filters/Filters.styled';
 import { InputLabel } from 'components/common/Input/InputLabel.styled';
 import { InputLabel } from 'components/common/Input/InputLabel.styled';
 import Input from 'components/common/Input/Input';
 import Input from 'components/common/Input/Input';
 import { FormProvider, Controller, useForm } from 'react-hook-form';
 import { FormProvider, Controller, useForm } from 'react-hook-form';
 import { ErrorMessage } from '@hookform/error-message';
 import { ErrorMessage } from '@hookform/error-message';
 import { Button } from 'components/common/Button/Button';
 import { Button } from 'components/common/Button/Button';
 import { FormError } from 'components/common/Input/Input.styled';
 import { FormError } from 'components/common/Input/Input.styled';
-import { AddMessageFilters } from 'components/Topics/Topic/Details/Messages/Filters/AddFilter';
+import { AddMessageFilters } from 'components/Topics/Topic/Messages/Filters/AddFilter';
 import Editor from 'components/common/Editor/Editor';
 import Editor from 'components/common/Editor/Editor';
 import { yupResolver } from '@hookform/resolvers/yup';
 import { yupResolver } from '@hookform/resolvers/yup';
 import yup from 'lib/yupExtended';
 import yup from 'lib/yupExtended';

+ 4 - 4
kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/Filters/AddFilter.tsx → kafka-ui-react-app/src/components/Topics/Topic/Messages/Filters/AddFilter.tsx

@@ -1,8 +1,8 @@
 import React from 'react';
 import React from 'react';
-import * as S from 'components/Topics/Topic/Details/Messages/Filters/Filters.styled';
-import { MessageFilters } from 'components/Topics/Topic/Details/Messages/Filters/Filters';
-import { FilterEdit } from 'components/Topics/Topic/Details/Messages/Filters/FilterModal';
-import SavedFilters from 'components/Topics/Topic/Details/Messages/Filters/SavedFilters';
+import * as S from 'components/Topics/Topic/Messages/Filters/Filters.styled';
+import { MessageFilters } from 'components/Topics/Topic/Messages/Filters/Filters';
+import { FilterEdit } from 'components/Topics/Topic/Messages/Filters/FilterModal';
+import SavedFilters from 'components/Topics/Topic/Messages/Filters/SavedFilters';
 import SavedIcon from 'components/common/Icons/SavedIcon';
 import SavedIcon from 'components/common/Icons/SavedIcon';
 import QuestionIcon from 'components/common/Icons/QuestionIcon';
 import QuestionIcon from 'components/common/Icons/QuestionIcon';
 import useModal from 'lib/hooks/useModal';
 import useModal from 'lib/hooks/useModal';

+ 2 - 2
kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/Filters/EditFilter.tsx → kafka-ui-react-app/src/components/Topics/Topic/Messages/Filters/EditFilter.tsx

@@ -1,6 +1,6 @@
 import React from 'react';
 import React from 'react';
-import { MessageFilters } from 'components/Topics/Topic/Details/Messages/Filters/Filters';
-import { FilterEdit } from 'components/Topics/Topic/Details/Messages/Filters/FilterModal';
+import { MessageFilters } from 'components/Topics/Topic/Messages/Filters/Filters';
+import { FilterEdit } from 'components/Topics/Topic/Messages/Filters/FilterModal';
 
 
 import AddEditFilterContainer from './AddEditFilterContainer';
 import AddEditFilterContainer from './AddEditFilterContainer';
 import * as S from './Filters.styled';
 import * as S from './Filters.styled';

+ 4 - 4
kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/Filters/FilterModal.tsx → kafka-ui-react-app/src/components/Topics/Topic/Messages/Filters/FilterModal.tsx

@@ -1,8 +1,8 @@
 import React from 'react';
 import React from 'react';
-import * as S from 'components/Topics/Topic/Details/Messages/Filters/Filters.styled';
-import { MessageFilters } from 'components/Topics/Topic/Details/Messages/Filters/Filters';
-import AddFilter from 'components/Topics/Topic/Details/Messages/Filters/AddFilter';
-import EditFilter from 'components/Topics/Topic/Details/Messages/Filters/EditFilter';
+import * as S from 'components/Topics/Topic/Messages/Filters/Filters.styled';
+import { MessageFilters } from 'components/Topics/Topic/Messages/Filters/Filters';
+import AddFilter from 'components/Topics/Topic/Messages/Filters/AddFilter';
+import EditFilter from 'components/Topics/Topic/Messages/Filters/EditFilter';
 
 
 export interface FilterModalProps {
 export interface FilterModalProps {
   toggleIsOpen(): void;
   toggleIsOpen(): void;

+ 2 - 6
kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/Filters/Filters.styled.ts → kafka-ui-react-app/src/components/Topics/Topic/Messages/Filters/Filters.styled.ts

@@ -134,7 +134,7 @@ export const MessageFilterModal = styled.div`
   background: ${({ theme }) => theme.modal.backgroundColor};
   background: ${({ theme }) => theme.modal.backgroundColor};
   position: absolute;
   position: absolute;
   left: 25%;
   left: 25%;
-  border: 1px solid ${({ theme }) => theme.breadcrumb};
+  border: 1px solid ${({ theme }) => theme.modal.border.contrast};
   box-shadow: ${({ theme }) => theme.modal.shadow};
   box-shadow: ${({ theme }) => theme.modal.shadow};
   padding: 16px;
   padding: 16px;
   z-index: 1;
   z-index: 1;
@@ -147,7 +147,7 @@ export const InfoModal = styled.div`
   background: ${({ theme }) => theme.modal.backgroundColor};
   background: ${({ theme }) => theme.modal.backgroundColor};
   position: absolute;
   position: absolute;
   left: 25%;
   left: 25%;
-  border: 1px solid ${({ theme }) => theme.breadcrumb};
+  border: 1px solid ${({ theme }) => theme.modal.border.contrast};
   box-shadow: ${({ theme }) => theme.modal.shadow};
   box-shadow: ${({ theme }) => theme.modal.shadow};
   padding: 32px;
   padding: 32px;
   z-index: 1;
   z-index: 1;
@@ -182,7 +182,6 @@ export const FilterTitle = styled.h3`
 
 
 export const CreatedFilter = styled.p`
 export const CreatedFilter = styled.p`
   margin: 25px 0 10px;
   margin: 25px 0 10px;
-  color: ${({ theme }) => theme.breadcrumb};
   font-size: 14px;
   font-size: 14px;
   line-height: 20px;
   line-height: 20px;
 `;
 `;
@@ -190,11 +189,8 @@ export const CreatedFilter = styled.p`
 export const SavedFiltersContainer = styled.div`
 export const SavedFiltersContainer = styled.div`
   overflow-y: auto;
   overflow-y: auto;
   height: 195px;
   height: 195px;
-  // display: flex;
-  // flex-direction: column;
   justify-content: space-around;
   justify-content: space-around;
   padding-left: 10px;
   padding-left: 10px;
-  // gap: 10px;
 `;
 `;
 
 
 export const SavedFilterName = styled.div`
 export const SavedFilterName = styled.div`

+ 2 - 2
kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/Filters/Filters.tsx → kafka-ui-react-app/src/components/Topics/Topic/Messages/Filters/Filters.tsx

@@ -22,8 +22,8 @@ import { Button } from 'components/common/Button/Button';
 import Search from 'components/common/Search/Search';
 import Search from 'components/common/Search/Search';
 import FilterModal, {
 import FilterModal, {
   FilterEdit,
   FilterEdit,
-} from 'components/Topics/Topic/Details/Messages/Filters/FilterModal';
-import { SeekDirectionOptions } from 'components/Topics/Topic/Details/Messages/Messages';
+} from 'components/Topics/Topic/Messages/Filters/FilterModal';
+import { SeekDirectionOptions } from 'components/Topics/Topic/Messages/Messages';
 import TopicMessagesContext from 'components/contexts/TopicMessagesContext';
 import TopicMessagesContext from 'components/contexts/TopicMessagesContext';
 import useModal from 'lib/hooks/useModal';
 import useModal from 'lib/hooks/useModal';
 import { RouteParamsClusterTopic } from 'lib/paths';
 import { RouteParamsClusterTopic } from 'lib/paths';

+ 0 - 0
kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/Filters/FiltersContainer.ts → kafka-ui-react-app/src/components/Topics/Topic/Messages/Filters/FiltersContainer.ts


+ 1 - 1
kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/Filters/InfoModal.tsx → kafka-ui-react-app/src/components/Topics/Topic/Messages/Filters/InfoModal.tsx

@@ -1,5 +1,5 @@
 import React from 'react';
 import React from 'react';
-import * as S from 'components/Topics/Topic/Details/Messages/Filters/Filters.styled';
+import * as S from 'components/Topics/Topic/Messages/Filters/Filters.styled';
 import { Button } from 'components/common/Button/Button';
 import { Button } from 'components/common/Button/Button';
 
 
 interface InfoModalProps {
 interface InfoModalProps {

+ 0 - 0
kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/Filters/SavedFilters.tsx → kafka-ui-react-app/src/components/Topics/Topic/Messages/Filters/SavedFilters.tsx


+ 2 - 2
kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/Filters/__tests__/AddEditFilterContainer.spec.tsx → kafka-ui-react-app/src/components/Topics/Topic/Messages/Filters/__tests__/AddEditFilterContainer.spec.tsx

@@ -1,11 +1,11 @@
 import React from 'react';
 import React from 'react';
 import AddEditFilterContainer, {
 import AddEditFilterContainer, {
   AddEditFilterContainerProps,
   AddEditFilterContainerProps,
-} from 'components/Topics/Topic/Details/Messages/Filters/AddEditFilterContainer';
+} from 'components/Topics/Topic/Messages/Filters/AddEditFilterContainer';
 import { render } from 'lib/testHelpers';
 import { render } from 'lib/testHelpers';
 import { act, screen } from '@testing-library/react';
 import { act, screen } from '@testing-library/react';
 import userEvent from '@testing-library/user-event';
 import userEvent from '@testing-library/user-event';
-import { MessageFilters } from 'components/Topics/Topic/Details/Messages/Filters/Filters';
+import { MessageFilters } from 'components/Topics/Topic/Messages/Filters/Filters';
 
 
 describe('AddEditFilterContainer component', () => {
 describe('AddEditFilterContainer component', () => {
   const defaultSubmitBtn = 'Submit Button';
   const defaultSubmitBtn = 'Submit Button';

+ 2 - 2
kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/Filters/__tests__/AddFilter.spec.tsx → kafka-ui-react-app/src/components/Topics/Topic/Messages/Filters/__tests__/AddFilter.spec.tsx

@@ -1,9 +1,9 @@
 import React from 'react';
 import React from 'react';
 import AddFilter, {
 import AddFilter, {
   FilterModalProps,
   FilterModalProps,
-} from 'components/Topics/Topic/Details/Messages/Filters/AddFilter';
+} from 'components/Topics/Topic/Messages/Filters/AddFilter';
 import { render } from 'lib/testHelpers';
 import { render } from 'lib/testHelpers';
-import { MessageFilters } from 'components/Topics/Topic/Details/Messages/Filters/Filters';
+import { MessageFilters } from 'components/Topics/Topic/Messages/Filters/Filters';
 import { act, screen } from '@testing-library/react';
 import { act, screen } from '@testing-library/react';
 import userEvent from '@testing-library/user-event';
 import userEvent from '@testing-library/user-event';
 
 

+ 2 - 2
kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/Filters/__tests__/EditFilter.spec.tsx → kafka-ui-react-app/src/components/Topics/Topic/Messages/Filters/__tests__/EditFilter.spec.tsx

@@ -1,11 +1,11 @@
 import React from 'react';
 import React from 'react';
 import EditFilter, {
 import EditFilter, {
   EditFilterProps,
   EditFilterProps,
-} from 'components/Topics/Topic/Details/Messages/Filters/EditFilter';
+} from 'components/Topics/Topic/Messages/Filters/EditFilter';
 import { render } from 'lib/testHelpers';
 import { render } from 'lib/testHelpers';
 import { screen, fireEvent, within, act } from '@testing-library/react';
 import { screen, fireEvent, within, act } from '@testing-library/react';
 import userEvent from '@testing-library/user-event';
 import userEvent from '@testing-library/user-event';
-import { FilterEdit } from 'components/Topics/Topic/Details/Messages/Filters/FilterModal';
+import { FilterEdit } from 'components/Topics/Topic/Messages/Filters/FilterModal';
 
 
 const editFilter: FilterEdit = {
 const editFilter: FilterEdit = {
   index: 0,
   index: 0,

+ 2 - 2
kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/Filters/__tests__/FilterModal.spec.tsx → kafka-ui-react-app/src/components/Topics/Topic/Messages/Filters/__tests__/FilterModal.spec.tsx

@@ -1,9 +1,9 @@
 import React from 'react';
 import React from 'react';
 import FilterModal, {
 import FilterModal, {
   FilterModalProps,
   FilterModalProps,
-} from 'components/Topics/Topic/Details/Messages/Filters/FilterModal';
+} from 'components/Topics/Topic/Messages/Filters/FilterModal';
 import { render } from 'lib/testHelpers';
 import { render } from 'lib/testHelpers';
-import { MessageFilters } from 'components/Topics/Topic/Details/Messages/Filters/Filters';
+import { MessageFilters } from 'components/Topics/Topic/Messages/Filters/Filters';
 import { screen, act } from '@testing-library/react';
 import { screen, act } from '@testing-library/react';
 import userEvent from '@testing-library/user-event';
 import userEvent from '@testing-library/user-event';
 
 

+ 2 - 2
kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/Filters/__tests__/Filters.spec.tsx → kafka-ui-react-app/src/components/Topics/Topic/Messages/Filters/__tests__/Filters.spec.tsx

@@ -1,9 +1,9 @@
 import React from 'react';
 import React from 'react';
-import { SeekDirectionOptions } from 'components/Topics/Topic/Details/Messages/Messages';
+import { SeekDirectionOptions } from 'components/Topics/Topic/Messages/Messages';
 import Filters, {
 import Filters, {
   FiltersProps,
   FiltersProps,
   SeekTypeOptions,
   SeekTypeOptions,
-} from 'components/Topics/Topic/Details/Messages/Filters/Filters';
+} from 'components/Topics/Topic/Messages/Filters/Filters';
 import { EventSourceMock, render, WithRoute } from 'lib/testHelpers';
 import { EventSourceMock, render, WithRoute } from 'lib/testHelpers';
 import { act, screen, within, waitFor } from '@testing-library/react';
 import { act, screen, within, waitFor } from '@testing-library/react';
 import userEvent from '@testing-library/user-event';
 import userEvent from '@testing-library/user-event';

+ 1 - 1
kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/Filters/__tests__/Filters.styled.spec.tsx → kafka-ui-react-app/src/components/Topics/Topic/Messages/Filters/__tests__/Filters.styled.spec.tsx

@@ -1,6 +1,6 @@
 import React from 'react';
 import React from 'react';
 import { render } from 'lib/testHelpers';
 import { render } from 'lib/testHelpers';
-import * as S from 'components/Topics/Topic/Details/Messages/Filters/Filters.styled';
+import * as S from 'components/Topics/Topic/Messages/Filters/Filters.styled';
 import { screen } from '@testing-library/react';
 import { screen } from '@testing-library/react';
 import theme from 'theme/theme';
 import theme from 'theme/theme';
 
 

+ 1 - 1
kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/Filters/__tests__/InfoModal.spec.tsx → kafka-ui-react-app/src/components/Topics/Topic/Messages/Filters/__tests__/InfoModal.spec.tsx

@@ -2,7 +2,7 @@ import userEvent from '@testing-library/user-event';
 import { screen } from '@testing-library/react';
 import { screen } from '@testing-library/react';
 import { render } from 'lib/testHelpers';
 import { render } from 'lib/testHelpers';
 import React from 'react';
 import React from 'react';
-import InfoModal from 'components/Topics/Topic/Details/Messages/Filters/InfoModal';
+import InfoModal from 'components/Topics/Topic/Messages/Filters/InfoModal';
 
 
 describe('InfoModal component', () => {
 describe('InfoModal component', () => {
   it('closes InfoModal', () => {
   it('closes InfoModal', () => {

+ 2 - 2
kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/Filters/__tests__/SavedFilters.spec.tsx → kafka-ui-react-app/src/components/Topics/Topic/Messages/Filters/__tests__/SavedFilters.spec.tsx

@@ -1,8 +1,8 @@
 import React from 'react';
 import React from 'react';
 import SavedFilters, {
 import SavedFilters, {
   Props,
   Props,
-} from 'components/Topics/Topic/Details/Messages/Filters/SavedFilters';
-import { MessageFilters } from 'components/Topics/Topic/Details/Messages/Filters/Filters';
+} from 'components/Topics/Topic/Messages/Filters/SavedFilters';
+import { MessageFilters } from 'components/Topics/Topic/Messages/Filters/Filters';
 import {
 import {
   screen,
   screen,
   waitFor,
   waitFor,

+ 0 - 0
kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/Filters/utils.ts → kafka-ui-react-app/src/components/Topics/Topic/Messages/Filters/utils.ts


+ 0 - 0
kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/Message.tsx → kafka-ui-react-app/src/components/Topics/Topic/Messages/Message.tsx


+ 0 - 0
kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/MessageContent/MessageContent.styled.ts → kafka-ui-react-app/src/components/Topics/Topic/Messages/MessageContent/MessageContent.styled.ts


+ 0 - 0
kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/MessageContent/MessageContent.tsx → kafka-ui-react-app/src/components/Topics/Topic/Messages/MessageContent/MessageContent.tsx


+ 1 - 1
kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/MessageContent/__tests__/MessageContent.spec.tsx → kafka-ui-react-app/src/components/Topics/Topic/Messages/MessageContent/__tests__/MessageContent.spec.tsx

@@ -4,7 +4,7 @@ import React from 'react';
 import { screen } from '@testing-library/react';
 import { screen } from '@testing-library/react';
 import MessageContent, {
 import MessageContent, {
   MessageContentProps,
   MessageContentProps,
-} from 'components/Topics/Topic/Details/Messages/MessageContent/MessageContent';
+} from 'components/Topics/Topic/Messages/MessageContent/MessageContent';
 import { TopicMessageTimestampTypeEnum } from 'generated-sources';
 import { TopicMessageTimestampTypeEnum } from 'generated-sources';
 import userEvent from '@testing-library/user-event';
 import userEvent from '@testing-library/user-event';
 import { render } from 'lib/testHelpers';
 import { render } from 'lib/testHelpers';

+ 0 - 0
kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/Messages.tsx → kafka-ui-react-app/src/components/Topics/Topic/Messages/Messages.tsx


+ 0 - 0
kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/MessagesTable.tsx → kafka-ui-react-app/src/components/Topics/Topic/Messages/MessagesTable.tsx


+ 4 - 5
kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/__test__/FiltersContainer.spec.tsx → kafka-ui-react-app/src/components/Topics/Topic/Messages/__test__/FiltersContainer.spec.tsx

@@ -1,12 +1,11 @@
 import React from 'react';
 import React from 'react';
-import FiltersContainer from 'components/Topics/Topic/Details/Messages/Filters/FiltersContainer';
+import FiltersContainer from 'components/Topics/Topic/Messages/Filters/FiltersContainer';
 import { screen } from '@testing-library/react';
 import { screen } from '@testing-library/react';
 import { render } from 'lib/testHelpers';
 import { render } from 'lib/testHelpers';
 
 
-jest.mock(
-  'components/Topics/Topic/Details/Messages/Filters/Filters',
-  () => () => <div>mock-Filters</div>
-);
+jest.mock('components/Topics/Topic/Messages/Filters/Filters', () => () => (
+  <div>mock-Filters</div>
+));
 
 
 describe('FiltersContainer', () => {
 describe('FiltersContainer', () => {
   it('renders Filters component', () => {
   it('renders Filters component', () => {

+ 2 - 4
kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/__test__/Message.spec.tsx → kafka-ui-react-app/src/components/Topics/Topic/Messages/__test__/Message.spec.tsx

@@ -1,8 +1,6 @@
 import React from 'react';
 import React from 'react';
 import { TopicMessage, TopicMessageTimestampTypeEnum } from 'generated-sources';
 import { TopicMessage, TopicMessageTimestampTypeEnum } from 'generated-sources';
-import Message, {
-  Props,
-} from 'components/Topics/Topic/Details/Messages/Message';
+import Message, { Props } from 'components/Topics/Topic/Messages/Message';
 import { screen } from '@testing-library/react';
 import { screen } from '@testing-library/react';
 import { render } from 'lib/testHelpers';
 import { render } from 'lib/testHelpers';
 import userEvent from '@testing-library/user-event';
 import userEvent from '@testing-library/user-event';
@@ -11,7 +9,7 @@ import { formatTimestamp } from 'lib/dateTimeHelpers';
 const messageContentText = 'messageContentText';
 const messageContentText = 'messageContentText';
 
 
 jest.mock(
 jest.mock(
-  'components/Topics/Topic/Details/Messages/MessageContent/MessageContent',
+  'components/Topics/Topic/Messages/MessageContent/MessageContent',
   () => () =>
   () => () =>
     (
     (
       <tr>
       <tr>

+ 1 - 1
kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/__test__/Messages.spec.tsx → kafka-ui-react-app/src/components/Topics/Topic/Messages/__test__/Messages.spec.tsx

@@ -4,7 +4,7 @@ import { render, EventSourceMock, WithRoute } from 'lib/testHelpers';
 import Messages, {
 import Messages, {
   SeekDirectionOptions,
   SeekDirectionOptions,
   SeekDirectionOptionsObj,
   SeekDirectionOptionsObj,
-} from 'components/Topics/Topic/Details/Messages/Messages';
+} from 'components/Topics/Topic/Messages/Messages';
 import { SeekDirection, SeekType } from 'generated-sources';
 import { SeekDirection, SeekType } from 'generated-sources';
 import userEvent from '@testing-library/user-event';
 import userEvent from '@testing-library/user-event';
 import { clusterTopicMessagesPath } from 'lib/paths';
 import { clusterTopicMessagesPath } from 'lib/paths';

+ 1 - 1
kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/__test__/MessagesTable.spec.tsx → kafka-ui-react-app/src/components/Topics/Topic/Messages/__test__/MessagesTable.spec.tsx

@@ -1,7 +1,7 @@
 import React from 'react';
 import React from 'react';
 import { screen } from '@testing-library/react';
 import { screen } from '@testing-library/react';
 import { render } from 'lib/testHelpers';
 import { render } from 'lib/testHelpers';
-import MessagesTable from 'components/Topics/Topic/Details/Messages/MessagesTable';
+import MessagesTable from 'components/Topics/Topic/Messages/MessagesTable';
 import { SeekDirection, SeekType, TopicMessage } from 'generated-sources';
 import { SeekDirection, SeekType, TopicMessage } from 'generated-sources';
 import TopicMessagesContext, {
 import TopicMessagesContext, {
   ContextProps,
   ContextProps,

+ 1 - 1
kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/__test__/utils.spec.ts → kafka-ui-react-app/src/components/Topics/Topic/Messages/__test__/utils.spec.ts

@@ -4,7 +4,7 @@ import {
   getOffsetFromSeekToParam,
   getOffsetFromSeekToParam,
   getTimestampFromSeekToParam,
   getTimestampFromSeekToParam,
   getSelectedPartitionsFromSeekToParam,
   getSelectedPartitionsFromSeekToParam,
-} from 'components/Topics/Topic/Details/Messages/Filters/utils';
+} from 'components/Topics/Topic/Messages/Filters/utils';
 import { SeekType, Partition } from 'generated-sources';
 import { SeekType, Partition } from 'generated-sources';
 
 
 const options: Option[] = [
 const options: Option[] = [

+ 0 - 0
kafka-ui-react-app/src/components/Topics/Topic/Details/Overview/Overview.styled.ts → kafka-ui-react-app/src/components/Topics/Topic/Overview/Overview.styled.ts


+ 0 - 0
kafka-ui-react-app/src/components/Topics/Topic/Details/Overview/Overview.tsx → kafka-ui-react-app/src/components/Topics/Topic/Overview/Overview.tsx


+ 2 - 2
kafka-ui-react-app/src/components/Topics/Topic/Details/Overview/__test__/Overview.spec.tsx → kafka-ui-react-app/src/components/Topics/Topic/Overview/__test__/Overview.spec.tsx

@@ -1,13 +1,13 @@
 import React from 'react';
 import React from 'react';
 import { screen } from '@testing-library/react';
 import { screen } from '@testing-library/react';
 import { render, WithRoute } from 'lib/testHelpers';
 import { render, WithRoute } from 'lib/testHelpers';
-import Overview from 'components/Topics/Topic/Details/Overview/Overview';
+import Overview from 'components/Topics/Topic/Overview/Overview';
 import theme from 'theme/theme';
 import theme from 'theme/theme';
 import { CleanUpPolicy, Topic } from 'generated-sources';
 import { CleanUpPolicy, Topic } from 'generated-sources';
 import ClusterContext from 'components/contexts/ClusterContext';
 import ClusterContext from 'components/contexts/ClusterContext';
 import userEvent from '@testing-library/user-event';
 import userEvent from '@testing-library/user-event';
 import { clusterTopicPath } from 'lib/paths';
 import { clusterTopicPath } from 'lib/paths';
-import { Replica } from 'components/Topics/Topic/Details/Overview/Overview.styled';
+import { Replica } from 'components/Topics/Topic/Overview/Overview.styled';
 import { useTopicDetails } from 'lib/hooks/api/topics';
 import { useTopicDetails } from 'lib/hooks/api/topics';
 import {
 import {
   externalTopicPayload,
   externalTopicPayload,

+ 0 - 0
kafka-ui-react-app/src/components/Topics/Topic/Details/Settings/ConfigListItem.tsx → kafka-ui-react-app/src/components/Topics/Topic/Settings/ConfigListItem.tsx


+ 0 - 0
kafka-ui-react-app/src/components/Topics/Topic/Details/Settings/Settings.styled.ts → kafka-ui-react-app/src/components/Topics/Topic/Settings/Settings.styled.ts


+ 0 - 0
kafka-ui-react-app/src/components/Topics/Topic/Details/Settings/Settings.tsx → kafka-ui-react-app/src/components/Topics/Topic/Settings/Settings.tsx


+ 1 - 1
kafka-ui-react-app/src/components/Topics/Topic/Details/Settings/__test__/ConfigListItem.spec.tsx → kafka-ui-react-app/src/components/Topics/Topic/Settings/__test__/ConfigListItem.spec.tsx

@@ -3,7 +3,7 @@ import { render } from 'lib/testHelpers';
 import { screen } from '@testing-library/react';
 import { screen } from '@testing-library/react';
 import ConfigListItem, {
 import ConfigListItem, {
   ListItemProps,
   ListItemProps,
-} from 'components/Topics/Topic/Details/Settings/ConfigListItem';
+} from 'components/Topics/Topic/Settings/ConfigListItem';
 
 
 const setupComponent = (props: ListItemProps) => {
 const setupComponent = (props: ListItemProps) => {
   render(
   render(

+ 6 - 10
kafka-ui-react-app/src/components/Topics/Topic/Details/Settings/__test__/Settings.spec.tsx → kafka-ui-react-app/src/components/Topics/Topic/Settings/__test__/Settings.spec.tsx

@@ -1,7 +1,7 @@
 import React from 'react';
 import React from 'react';
 import { render, WithRoute } from 'lib/testHelpers';
 import { render, WithRoute } from 'lib/testHelpers';
 import { screen } from '@testing-library/react';
 import { screen } from '@testing-library/react';
-import Settings from 'components/Topics/Topic/Details/Settings/Settings';
+import Settings from 'components/Topics/Topic/Settings/Settings';
 import { clusterTopicSettingsPath } from 'lib/paths';
 import { clusterTopicSettingsPath } from 'lib/paths';
 import { topicConfigPayload } from 'lib/fixtures/topics';
 import { topicConfigPayload } from 'lib/fixtures/topics';
 import { useTopicConfig } from 'lib/hooks/api/topics';
 import { useTopicConfig } from 'lib/hooks/api/topics';
@@ -13,15 +13,11 @@ jest.mock('lib/hooks/api/topics', () => ({
   useTopicConfig: jest.fn(),
   useTopicConfig: jest.fn(),
 }));
 }));
 
 
-jest.mock(
-  'components/Topics/Topic/Details/Settings/ConfigListItem',
-  () => () =>
-    (
-      <tr>
-        <td>ConfigListItemMock</td>
-      </tr>
-    )
-);
+jest.mock('components/Topics/Topic/Settings/ConfigListItem', () => () => (
+  <tr>
+    <td>ConfigListItemMock</td>
+  </tr>
+));
 
 
 describe('Settings', () => {
 describe('Settings', () => {
   const renderComponent = () => {
   const renderComponent = () => {

+ 0 - 0
kafka-ui-react-app/src/components/Topics/Topic/Details/Statistics/Indicators/SizeStats.tsx → kafka-ui-react-app/src/components/Topics/Topic/Statistics/Indicators/SizeStats.tsx


+ 0 - 0
kafka-ui-react-app/src/components/Topics/Topic/Details/Statistics/Indicators/Total.tsx → kafka-ui-react-app/src/components/Topics/Topic/Statistics/Indicators/Total.tsx


+ 0 - 0
kafka-ui-react-app/src/components/Topics/Topic/Details/Statistics/Metrics.tsx → kafka-ui-react-app/src/components/Topics/Topic/Statistics/Metrics.tsx


+ 0 - 0
kafka-ui-react-app/src/components/Topics/Topic/Details/Statistics/PartitionInfoRow.tsx → kafka-ui-react-app/src/components/Topics/Topic/Statistics/PartitionInfoRow.tsx


+ 0 - 0
kafka-ui-react-app/src/components/Topics/Topic/Details/Statistics/PartitionTable.tsx → kafka-ui-react-app/src/components/Topics/Topic/Statistics/PartitionTable.tsx


+ 0 - 0
kafka-ui-react-app/src/components/Topics/Topic/Details/Statistics/Statistics.styles.ts → kafka-ui-react-app/src/components/Topics/Topic/Statistics/Statistics.styles.ts


+ 0 - 0
kafka-ui-react-app/src/components/Topics/Topic/Details/Statistics/Statistics.tsx → kafka-ui-react-app/src/components/Topics/Topic/Statistics/Statistics.tsx


+ 1 - 1
kafka-ui-react-app/src/components/Topics/Topic/Details/Statistics/__test__/Metrics.spec.tsx → kafka-ui-react-app/src/components/Topics/Topic/Statistics/__test__/Metrics.spec.tsx

@@ -1,7 +1,7 @@
 import React from 'react';
 import React from 'react';
 import { screen, waitFor } from '@testing-library/react';
 import { screen, waitFor } from '@testing-library/react';
 import { render, WithRoute } from 'lib/testHelpers';
 import { render, WithRoute } from 'lib/testHelpers';
-import Statistics from 'components/Topics/Topic/Details/Statistics/Statistics';
+import Statistics from 'components/Topics/Topic/Statistics/Statistics';
 import { clusterTopicStatisticsPath } from 'lib/paths';
 import { clusterTopicStatisticsPath } from 'lib/paths';
 import {
 import {
   useTopicAnalysis,
   useTopicAnalysis,

+ 1 - 1
kafka-ui-react-app/src/components/Topics/Topic/Details/Statistics/__test__/Statistics.spec.tsx → kafka-ui-react-app/src/components/Topics/Topic/Statistics/__test__/Statistics.spec.tsx

@@ -1,7 +1,7 @@
 import React from 'react';
 import React from 'react';
 import { screen } from '@testing-library/react';
 import { screen } from '@testing-library/react';
 import { render, WithRoute } from 'lib/testHelpers';
 import { render, WithRoute } from 'lib/testHelpers';
-import Statistics from 'components/Topics/Topic/Details/Statistics/Statistics';
+import Statistics from 'components/Topics/Topic/Statistics/Statistics';
 import { clusterTopicStatisticsPath } from 'lib/paths';
 import { clusterTopicStatisticsPath } from 'lib/paths';
 import { useTopicAnalysis } from 'lib/hooks/api/topics';
 import { useTopicAnalysis } from 'lib/hooks/api/topics';
 
 

+ 183 - 15
kafka-ui-react-app/src/components/Topics/Topic/Topic.tsx

@@ -1,36 +1,204 @@
 import React, { Suspense } from 'react';
 import React, { Suspense } from 'react';
-import { Routes, Route } from 'react-router-dom';
+import { NavLink, Route, Routes, useNavigate } from 'react-router-dom';
 import {
 import {
+  RouteParamsClusterTopic,
+  clusterTopicMessagesRelativePath,
+  clusterTopicSettingsRelativePath,
+  clusterTopicConsumerGroupsRelativePath,
   clusterTopicEditRelativePath,
   clusterTopicEditRelativePath,
   clusterTopicSendMessageRelativePath,
   clusterTopicSendMessageRelativePath,
+  clusterTopicStatisticsRelativePath,
+  clusterTopicsPath,
+  clusterTopicSendMessagePath,
 } from 'lib/paths';
 } from 'lib/paths';
-import PageLoader from 'components/common/PageLoader/PageLoader';
-import { resetTopicMessages } from 'redux/reducers/topicMessages/topicMessagesSlice';
+import ClusterContext from 'components/contexts/ClusterContext';
+import PageHeading from 'components/common/PageHeading/PageHeading';
+import { Button } from 'components/common/Button/Button';
+import Navbar from 'components/common/Navigation/Navbar.styled';
 import { useAppDispatch } from 'lib/hooks/redux';
 import { useAppDispatch } from 'lib/hooks/redux';
+import useAppParams from 'lib/hooks/useAppParams';
+import {
+  Dropdown,
+  DropdownItem,
+  DropdownItemHint,
+} from 'components/common/Dropdown';
+import {
+  useDeleteTopic,
+  useRecreateTopic,
+  useTopicDetails,
+} from 'lib/hooks/api/topics';
+import {
+  clearTopicMessages,
+  resetTopicMessages,
+} from 'redux/reducers/topicMessages/topicMessagesSlice';
+import { CleanUpPolicy } from 'generated-sources';
+import PageLoader from 'components/common/PageLoader/PageLoader';
 
 
-import SendMessage from './SendMessage/SendMessage';
-import Details from './Details/Details';
+import Messages from './Messages/Messages';
+import Overview from './Overview/Overview';
+import Settings from './Settings/Settings';
+import TopicConsumerGroups from './ConsumerGroups/TopicConsumerGroups';
+import Statistics from './Statistics/Statistics';
 import Edit from './Edit/Edit';
 import Edit from './Edit/Edit';
+import SendMessage from './SendMessage/SendMessage';
 
 
 const Topic: React.FC = () => {
 const Topic: React.FC = () => {
   const dispatch = useAppDispatch();
   const dispatch = useAppDispatch();
+  const { clusterName, topicName } = useAppParams<RouteParamsClusterTopic>();
+  const navigate = useNavigate();
+  const deleteTopic = useDeleteTopic(clusterName);
+  const recreateTopic = useRecreateTopic({ clusterName, topicName });
+  const { data } = useTopicDetails({ clusterName, topicName });
+
+  const { isReadOnly, isTopicDeletionAllowed } =
+    React.useContext(ClusterContext);
+
+  const deleteTopicHandler = async () => {
+    await deleteTopic.mutateAsync(topicName);
+    navigate('../..');
+  };
+
   React.useEffect(() => {
   React.useEffect(() => {
     return () => {
     return () => {
       dispatch(resetTopicMessages());
       dispatch(resetTopicMessages());
     };
     };
   }, []);
   }, []);
 
 
+  const canCleanup = data?.cleanUpPolicy === CleanUpPolicy.DELETE;
+
   return (
   return (
-    <Suspense fallback={<PageLoader />}>
-      <Routes>
-        <Route path="*" element={<Details />} />
-        <Route path={clusterTopicEditRelativePath} element={<Edit />} />
-        <Route
-          path={clusterTopicSendMessageRelativePath}
-          element={<SendMessage />}
-        />
-      </Routes>
-    </Suspense>
+    <>
+      <PageHeading
+        text={topicName}
+        backText="Topics"
+        backTo={clusterTopicsPath(clusterName)}
+      >
+        <Button
+          buttonSize="M"
+          buttonType="primary"
+          to={clusterTopicSendMessagePath(clusterName, topicName)}
+          disabled={isReadOnly}
+        >
+          Produce Message
+        </Button>
+        <Dropdown disabled={isReadOnly || data?.internal}>
+          <DropdownItem onClick={() => navigate(clusterTopicEditRelativePath)}>
+            Edit settings
+            <DropdownItemHint>
+              Pay attention! This operation has
+              <br />
+              especially important consequences.
+            </DropdownItemHint>
+          </DropdownItem>
+
+          <DropdownItem
+            onClick={() =>
+              dispatch(clearTopicMessages({ clusterName, topicName })).unwrap()
+            }
+            confirm="Are you sure want to clear topic messages?"
+            disabled={!canCleanup}
+            danger
+          >
+            Clear messages
+            <DropdownItemHint>
+              Clearing messages is only allowed for topics
+              <br />
+              with DELETE policy
+            </DropdownItemHint>
+          </DropdownItem>
+
+          <DropdownItem
+            onClick={recreateTopic.mutateAsync}
+            confirm={
+              <>
+                Are you sure want to recreate <b>{topicName}</b> topic?
+              </>
+            }
+            danger
+          >
+            Recreate Topic
+          </DropdownItem>
+          <DropdownItem
+            onClick={deleteTopicHandler}
+            confirm={
+              <>
+                Are you sure want to remove <b>{topicName}</b> topic?
+              </>
+            }
+            disabled={!isTopicDeletionAllowed}
+            danger
+          >
+            Remove Topic
+            {!isTopicDeletionAllowed && (
+              <DropdownItemHint>
+                The topic deletion is restricted at the application
+                <br />
+                configuration level
+              </DropdownItemHint>
+            )}
+          </DropdownItem>
+        </Dropdown>
+      </PageHeading>
+      <Navbar role="navigation">
+        <NavLink
+          to="."
+          className={({ isActive }) => (isActive ? 'is-active' : '')}
+          end
+        >
+          Overview
+        </NavLink>
+        <NavLink
+          to={clusterTopicMessagesRelativePath}
+          className={({ isActive }) => (isActive ? 'is-active' : '')}
+        >
+          Messages
+        </NavLink>
+        <NavLink
+          to={clusterTopicConsumerGroupsRelativePath}
+          className={({ isActive }) => (isActive ? 'is-active' : '')}
+        >
+          Consumers
+        </NavLink>
+        <NavLink
+          to={clusterTopicSettingsRelativePath}
+          className={({ isActive }) => (isActive ? 'is-active' : '')}
+        >
+          Settings
+        </NavLink>
+        <NavLink
+          to={clusterTopicStatisticsRelativePath}
+          className={({ isActive }) => (isActive ? 'is-active' : '')}
+        >
+          Statistics
+        </NavLink>
+      </Navbar>
+      <Suspense fallback={<PageLoader />}>
+        <Routes>
+          <Route index element={<Overview />} />
+          <Route
+            path={clusterTopicMessagesRelativePath}
+            element={<Messages />}
+          />
+          <Route
+            path={clusterTopicSettingsRelativePath}
+            element={<Settings />}
+          />
+          <Route
+            path={clusterTopicConsumerGroupsRelativePath}
+            element={<TopicConsumerGroups />}
+          />
+          <Route
+            path={clusterTopicStatisticsRelativePath}
+            element={<Statistics />}
+          />
+          <Route path={clusterTopicEditRelativePath} element={<Edit />} />
+          <Route
+            path={clusterTopicSendMessageRelativePath}
+            element={<SendMessage />}
+          />
+        </Routes>
+      </Suspense>
+    </>
   );
   );
 };
 };
 
 

+ 8 - 8
kafka-ui-react-app/src/components/Topics/Topic/Details/__test__/Details.spec.tsx → kafka-ui-react-app/src/components/Topics/Topic/__test__/Topic.spec.tsx

@@ -2,7 +2,7 @@ import React from 'react';
 import { act, screen, waitFor } from '@testing-library/react';
 import { act, screen, waitFor } from '@testing-library/react';
 import userEvent from '@testing-library/user-event';
 import userEvent from '@testing-library/user-event';
 import ClusterContext from 'components/contexts/ClusterContext';
 import ClusterContext from 'components/contexts/ClusterContext';
-import Details from 'components/Topics/Topic/Details/Details';
+import Details from 'components/Topics/Topic/Topic';
 import { render, WithRoute } from 'lib/testHelpers';
 import { render, WithRoute } from 'lib/testHelpers';
 import {
 import {
   clusterTopicConsumerGroupsPath,
   clusterTopicConsumerGroupsPath,
@@ -40,20 +40,20 @@ jest.mock('lib/hooks/redux', () => ({
   useAppDispatch: useDispatchMock,
   useAppDispatch: useDispatchMock,
 }));
 }));
 
 
-jest.mock('components/Topics/Topic/Details/Overview/Overview', () => () => (
+jest.mock('components/Topics/Topic/Overview/Overview', () => () => (
   <>OverviewMock</>
   <>OverviewMock</>
 ));
 ));
-jest.mock('components/Topics/Topic/Details/Messages/Messages', () => () => (
+jest.mock('components/Topics/Topic/Messages/Messages', () => () => (
   <>MessagesMock</>
   <>MessagesMock</>
 ));
 ));
-jest.mock('components/Topics/Topic/Details/Settings/Settings', () => () => (
+jest.mock('components/Topics/Topic/Settings/Settings', () => () => (
   <>SettingsMock</>
   <>SettingsMock</>
 ));
 ));
 jest.mock(
 jest.mock(
-  'components/Topics/Topic/Details/ConsumerGroups/TopicConsumerGroups',
+  'components/Topics/Topic/ConsumerGroups/TopicConsumerGroups',
   () => () => <>ConsumerGroupsMock</>
   () => () => <>ConsumerGroupsMock</>
 );
 );
-jest.mock('components/Topics/Topic/Details/Statistics/Statistics', () => () => (
+jest.mock('components/Topics/Topic/Statistics/Statistics', () => () => (
   <>StatisticsMock</>
   <>StatisticsMock</>
 ));
 ));
 
 
@@ -98,9 +98,9 @@ describe('Details', () => {
   });
   });
   describe('Action Bar', () => {
   describe('Action Bar', () => {
     describe('when it has readonly flag', () => {
     describe('when it has readonly flag', () => {
-      it('does not render the Action button a Topic', () => {
+      it('renders disabled the Action button a Topic', () => {
         renderComponent(true);
         renderComponent(true);
-        expect(screen.queryByText('Produce Message')).not.toBeInTheDocument();
+        expect(screen.getByText('Produce Message')).toBeDisabled();
       });
       });
     });
     });
 
 

+ 0 - 69
kafka-ui-react-app/src/components/Topics/Topic/__tests__/Topic.spec.tsx

@@ -1,69 +0,0 @@
-import React from 'react';
-import { render, WithRoute } from 'lib/testHelpers';
-import { screen } from '@testing-library/react';
-import Topic from 'components/Topics/Topic/Topic';
-import {
-  clusterTopicPath,
-  clusterTopicEditPath,
-  clusterTopicSendMessagePath,
-  getNonExactPath,
-} from 'lib/paths';
-import { useAppDispatch } from 'lib/hooks/redux';
-
-const topicText = {
-  edit: 'Edit',
-  send: 'Send Message',
-  detail: 'Details',
-  loading: 'Loading',
-};
-
-jest.mock('components/Topics/Topic/Edit/Edit', () => () => (
-  <div>{topicText.edit}</div>
-));
-jest.mock('components/Topics/Topic/SendMessage/SendMessage', () => () => (
-  <div>{topicText.send}</div>
-));
-jest.mock('components/Topics/Topic/Details/Details', () => () => (
-  <div>{topicText.detail}</div>
-));
-
-jest.mock('lib/hooks/redux', () => ({
-  ...jest.requireActual('lib/hooks/redux'),
-  useAppDispatch: jest.fn(),
-}));
-const useDispatchMock = jest.fn(jest.fn());
-
-describe('Topic Component', () => {
-  beforeEach(() => {
-    (useAppDispatch as jest.Mock).mockImplementation(() => useDispatchMock);
-  });
-
-  const renderComponent = (pathname: string) =>
-    render(
-      <WithRoute path={getNonExactPath(clusterTopicPath())}>
-        <Topic />
-      </WithRoute>,
-      { initialEntries: [pathname] }
-    );
-
-  it('renders Edit page', () => {
-    renderComponent(clusterTopicEditPath('local', 'myTopicName'));
-    expect(screen.getByText(topicText.edit)).toBeInTheDocument();
-  });
-
-  it('renders Send Message page', () => {
-    renderComponent(clusterTopicSendMessagePath('local', 'myTopicName'));
-    expect(screen.getByText(topicText.send)).toBeInTheDocument();
-  });
-
-  it('renders Details Container page', () => {
-    renderComponent(clusterTopicPath('local', 'myTopicName'));
-    expect(screen.getByText(topicText.detail)).toBeInTheDocument();
-  });
-
-  it('resets topic messages after unmount', () => {
-    const component = renderComponent(clusterTopicPath('local', 'myTopicName'));
-    component.unmount();
-    expect(useDispatchMock).toHaveBeenCalledTimes(1);
-  });
-});

+ 4 - 33
kafka-ui-react-app/src/components/Topics/Topics.tsx

@@ -6,7 +6,6 @@ import {
   getNonExactPath,
   getNonExactPath,
   RouteParams,
   RouteParams,
 } from 'lib/paths';
 } from 'lib/paths';
-import { BreadcrumbRoute } from 'components/common/Breadcrumb/Breadcrumb.route';
 
 
 import New from './New/New';
 import New from './New/New';
 import ListPage from './List/ListPage';
 import ListPage from './List/ListPage';
@@ -14,38 +13,10 @@ import Topic from './Topic/Topic';
 
 
 const Topics: React.FC = () => (
 const Topics: React.FC = () => (
   <Routes>
   <Routes>
-    <Route
-      index
-      element={
-        <BreadcrumbRoute>
-          <ListPage />
-        </BreadcrumbRoute>
-      }
-    />
-    <Route
-      path={clusterTopicNewRelativePath}
-      element={
-        <BreadcrumbRoute>
-          <New />
-        </BreadcrumbRoute>
-      }
-    />
-    <Route
-      path={clusterTopicCopyRelativePath}
-      element={
-        <BreadcrumbRoute>
-          <New />
-        </BreadcrumbRoute>
-      }
-    />
-    <Route
-      path={getNonExactPath(RouteParams.topicName)}
-      element={
-        <BreadcrumbRoute>
-          <Topic />
-        </BreadcrumbRoute>
-      }
-    />
+    <Route index element={<ListPage />} />
+    <Route path={clusterTopicNewRelativePath} element={<New />} />
+    <Route path={clusterTopicCopyRelativePath} element={<New />} />
+    <Route path={getNonExactPath(RouteParams.topicName)} element={<Topic />} />
   </Routes>
   </Routes>
 );
 );
 
 

+ 6 - 6
kafka-ui-react-app/src/components/Topics/shared/Form/TopicForm.tsx

@@ -232,20 +232,20 @@ const TopicForm: React.FC<Props> = ({
         <CustomParams isSubmitting={isSubmitting} />
         <CustomParams isSubmitting={isSubmitting} />
         <S.ButtonWrapper>
         <S.ButtonWrapper>
           <Button
           <Button
-            type="submit"
+            type="button"
             buttonType="primary"
             buttonType="primary"
             buttonSize="L"
             buttonSize="L"
-            disabled={!isValid || isSubmitting || !isDirty}
+            onClick={onCancel}
           >
           >
-            {isEditing ? 'Save' : 'Create topic'}
+            Cancel
           </Button>
           </Button>
           <Button
           <Button
-            type="button"
+            type="submit"
             buttonType="primary"
             buttonType="primary"
             buttonSize="L"
             buttonSize="L"
-            onClick={onCancel}
+            disabled={!isValid || isSubmitting || !isDirty}
           >
           >
-            Cancel
+            {isEditing ? 'Update topic' : 'Create topic'}
           </Button>
           </Button>
         </S.ButtonWrapper>
         </S.ButtonWrapper>
       </fieldset>
       </fieldset>

+ 0 - 16
kafka-ui-react-app/src/components/common/Breadcrumb/Breadcrumb.context.ts

@@ -1,16 +0,0 @@
-import { createContext } from 'react';
-
-export interface BreadcrumbEntry {
-  link: string;
-  path: string[];
-}
-
-interface BreadcrumbContextInterface extends BreadcrumbEntry {
-  handleRouteChange: (match: { url: string; path: string }) => void;
-}
-
-export const BreadcrumbContext = createContext<BreadcrumbContextInterface>({
-  link: '',
-  path: [],
-  handleRouteChange: () => {},
-});

+ 0 - 53
kafka-ui-react-app/src/components/common/Breadcrumb/Breadcrumb.provider.tsx

@@ -1,53 +0,0 @@
-import React, { PropsWithChildren, useState } from 'react';
-import capitalize from 'lodash/capitalize';
-
-import { BreadcrumbContext, BreadcrumbEntry } from './Breadcrumb.context';
-
-const mapLocationToPath = (
-  splittedLocation: string[],
-  splittedRoutePath: string[]
-) =>
-  splittedLocation.map((item, index) =>
-    splittedRoutePath[index]?.charAt(0) !== ':'
-      ? item.split('-').map(capitalize).join(' ')
-      : item
-  );
-
-export const BreadcrumbProvider: React.FC<PropsWithChildren<unknown>> = ({
-  children,
-}) => {
-  const [state, setState] = useState<BreadcrumbEntry>({
-    link: '',
-    path: [],
-  });
-
-  const handleRouteChange = (params: { url: string; path: string }) => {
-    setState((prevState) => {
-      const newState = { ...prevState };
-      const splittedRoutePath = params.path.split('/');
-      const splittedLocation = params.url.split('/');
-
-      if (prevState.link !== params.url) {
-        newState.link = params.url;
-        newState.path = mapLocationToPath(splittedLocation, splittedRoutePath);
-      }
-
-      if (prevState.path.length < params.path.split('/').length) {
-        newState.path = mapLocationToPath(splittedLocation, splittedRoutePath);
-      }
-
-      return newState;
-    });
-  };
-
-  return (
-    <BreadcrumbContext.Provider
-      value={{
-        ...state,
-        handleRouteChange,
-      }}
-    >
-      {children}
-    </BreadcrumbContext.Provider>
-  );
-};

+ 0 - 52
kafka-ui-react-app/src/components/common/Breadcrumb/Breadcrumb.route.tsx

@@ -1,52 +0,0 @@
-import React, { useContext, useEffect } from 'react';
-import { Params, Location, useLocation, useParams } from 'react-router-dom';
-
-import { BreadcrumbContext } from './Breadcrumb.context';
-
-const getRoutePath = (location: Location, params: Params): string => {
-  const { pathname } = location;
-
-  if (!Object.keys(params).length) {
-    return pathname; // we don't need to replace anything
-  }
-
-  let path = pathname;
-  Object.entries(params).forEach(([paramName, paramValue]) => {
-    if (paramValue) {
-      path = path.replace(paramValue, `:${paramName}`);
-    }
-  });
-  return path;
-};
-
-const BreadcrumbRouteInternal: React.FC = () => {
-  const location = useLocation();
-  const params = useParams();
-  const path = getRoutePath(location, params);
-  const context = useContext(BreadcrumbContext);
-
-  useEffect(() => {
-    context.handleRouteChange({
-      url: location.pathname,
-      path,
-    });
-    // eslint-disable-next-line react-hooks/exhaustive-deps
-  }, [location.pathname]);
-
-  return null;
-};
-
-interface BreadcrumbRoutProps {
-  children: React.ReactNode;
-}
-
-export const BreadcrumbRoute: React.FC<BreadcrumbRoutProps> = ({
-  children,
-}) => {
-  return (
-    <>
-      {children}
-      <BreadcrumbRouteInternal />
-    </>
-  );
-};

+ 0 - 15
kafka-ui-react-app/src/components/common/Breadcrumb/Breadcrumb.styled.ts

@@ -1,15 +0,0 @@
-import styled from 'styled-components';
-
-export const BreadcrumbWrapper = styled.ul`
-  display: flex;
-  padding-left: 16px;
-  padding-top: 1em;
-
-  font-size: 12px;
-
-  & li:not(:last-child)::after {
-    content: '/';
-    color: ${({ theme }) => theme.breadcrumb};
-    margin: 0 8px;
-  }
-`;

+ 0 - 49
kafka-ui-react-app/src/components/common/Breadcrumb/Breadcrumb.tsx

@@ -1,49 +0,0 @@
-import React, { useContext } from 'react';
-import { Link } from 'react-router-dom';
-import { clusterPath } from 'lib/paths';
-import { BREADCRUMB_DEFINITIONS } from 'lib/constants';
-
-import { BreadcrumbWrapper } from './Breadcrumb.styled';
-import { BreadcrumbContext } from './Breadcrumb.context';
-
-const basePathEntriesLength = clusterPath().split('/').length;
-
-export interface BreadcrumbDefinitions {
-  [key: string]: string;
-}
-
-const Breadcrumb: React.FC = () => {
-  const breadcrumbContext = useContext(BreadcrumbContext);
-
-  const links = React.useMemo(
-    () => breadcrumbContext.path.slice(basePathEntriesLength),
-    [breadcrumbContext.path]
-  );
-
-  const getPathPredicate = (index: number) =>
-    `${breadcrumbContext.link
-      .split('/')
-      .slice(0, basePathEntriesLength + index + 1)
-      .join('/')}`;
-
-  if (links.length < 2) {
-    return null;
-  }
-
-  return (
-    <BreadcrumbWrapper role="list">
-      {links.slice(0, links.length - 1).map((link, index) => (
-        <li key={link}>
-          <Link to={getPathPredicate(index)}>
-            {BREADCRUMB_DEFINITIONS[link] || link}
-          </Link>
-        </li>
-      ))}
-      <li>
-        <span>{links[links.length - 1]}</span>
-      </li>
-    </BreadcrumbWrapper>
-  );
-};
-
-export default Breadcrumb;

+ 0 - 40
kafka-ui-react-app/src/components/common/Breadcrumb/__tests__/Breadcrumb.spec.tsx

@@ -1,40 +0,0 @@
-import React from 'react';
-import Breadcrumb from 'components/common/Breadcrumb/Breadcrumb';
-import { BreadcrumbProvider } from 'components/common/Breadcrumb/Breadcrumb.provider';
-import { BreadcrumbRoute } from 'components/common/Breadcrumb/Breadcrumb.route';
-import { render, WithRoute } from 'lib/testHelpers';
-import { clusterTopicNewPath, clusterTopicPath } from 'lib/paths';
-
-const createTopicPath = clusterTopicNewPath('local');
-const createTopicRoutePath = clusterTopicNewPath();
-
-const topicName = 'topic-name';
-
-const topicPath = clusterTopicPath('secondLocal', topicName);
-const topicRoutePath = clusterTopicPath();
-
-describe('Breadcrumb component', () => {
-  const setupComponent = (pathname: string, routePath: string) =>
-    render(
-      <BreadcrumbProvider>
-        <Breadcrumb />
-        <WithRoute path={routePath}>
-          <BreadcrumbRoute>
-            <div />
-          </BreadcrumbRoute>
-        </WithRoute>
-      </BreadcrumbProvider>,
-      { initialEntries: [pathname] }
-    );
-
-  it('renders the list of links', async () => {
-    const { getByText } = setupComponent(createTopicPath, createTopicRoutePath);
-    expect(getByText('All Topics')).toBeInTheDocument();
-    expect(getByText('Create New Topic')).toBeInTheDocument();
-  });
-  it('renders the topic overview', async () => {
-    const { getByText } = setupComponent(topicPath, topicRoutePath);
-    expect(getByText('All Topics')).toBeInTheDocument();
-    expect(getByText(topicName)).toBeInTheDocument();
-  });
-});

+ 1 - 1
kafka-ui-react-app/src/components/common/ControlPanel/ControlPanel.styled.ts

@@ -8,7 +8,7 @@ export const ControlPanelWrapper = styled.div<Props>`
   display: flex;
   display: flex;
   align-items: center;
   align-items: center;
   padding: 0px 16px;
   padding: 0px 16px;
-  margin: 16px 0px;
+  margin: 0px 0px 16px;
   width: 100%;
   width: 100%;
   gap: 16px;
   gap: 16px;
   & > *:first-child {
   & > *:first-child {

+ 7 - 1
kafka-ui-react-app/src/components/common/Dropdown/Dropdown.styled.ts

@@ -62,7 +62,6 @@ export const DropdownButton = styled.button`
   display: flex;
   display: flex;
   cursor: pointer;
   cursor: pointer;
   align-self: center;
   align-self: center;
-  float: right;
 
 
   &:disabled {
   &:disabled {
     opacity: 0.5;
     opacity: 0.5;
@@ -77,5 +76,12 @@ export const DangerItem = styled.div`
 export const DropdownItemHint = styled.div`
 export const DropdownItemHint = styled.div`
   color: ${({ theme }) => theme.topicMetaData.color.label};
   color: ${({ theme }) => theme.topicMetaData.color.label};
   font-size: 12px;
   font-size: 12px;
+  line-height: 1.4;
   margin-top: 5px;
   margin-top: 5px;
 `;
 `;
+
+export const Wrapper = styled.div`
+  display: inline-flex;
+  align-items: center;
+  justify-content: end;
+`;

+ 2 - 2
kafka-ui-react-app/src/components/common/Dropdown/Dropdown.tsx

@@ -21,7 +21,7 @@ const Dropdown: React.FC<DropdownProps> = ({ label, disabled, children }) => {
   };
   };
 
 
   return (
   return (
-    <>
+    <S.Wrapper>
       <S.DropdownButton
       <S.DropdownButton
         onClick={handleClick}
         onClick={handleClick}
         ref={ref}
         ref={ref}
@@ -42,7 +42,7 @@ const Dropdown: React.FC<DropdownProps> = ({ label, disabled, children }) => {
       >
       >
         {children}
         {children}
       </S.Dropdown>
       </S.Dropdown>
-    </>
+    </S.Wrapper>
   );
   );
 };
 };
 
 

+ 1 - 2
kafka-ui-react-app/src/components/common/Form/Form.styled.ts

@@ -1,7 +1,6 @@
 import styled from 'styled-components';
 import styled from 'styled-components';
 
 
 export const StyledForm = styled.form`
 export const StyledForm = styled.form`
-  padding: 0 16px;
+  padding: 16px;
   max-width: 800px;
   max-width: 800px;
-  margin: 0 auto;
 `;
 `;

+ 42 - 0
kafka-ui-react-app/src/components/common/PageHeading/PageHeading.styled.ts

@@ -0,0 +1,42 @@
+import styled from 'styled-components';
+import { NavLink } from 'react-router-dom';
+
+export const Breadcrumbs = styled.div`
+  display: flex;
+  align-items: baseline;
+`;
+
+export const BackLink = styled(NavLink)`
+  color: ${({ theme }) => theme.pageHeading.backLink.color.normal};
+  position: relative;
+
+  &:hover {
+    ${({ theme }) => theme.pageHeading.backLink.color.hover};
+  }
+
+  &::after {
+    content: '';
+    position: absolute;
+    right: -11px;
+    bottom: 2px;
+    border-left: 1px solid ${({ theme }) => theme.pageHeading.dividerColor};
+    height: 20px;
+    transform: rotate(14deg);
+  }
+`;
+
+export const Wrapper = styled.div`
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 16px;
+
+  & > div {
+    display: flex;
+    gap: 16px;
+  }
+
+  & > ${Breadcrumbs} {
+    gap: 20px;
+  }
+`;

+ 17 - 19
kafka-ui-react-app/src/components/common/PageHeading/PageHeading.tsx

@@ -1,33 +1,31 @@
-import styled from 'styled-components';
 import React, { PropsWithChildren } from 'react';
 import React, { PropsWithChildren } from 'react';
 import Heading from 'components/common/heading/Heading.styled';
 import Heading from 'components/common/heading/Heading.styled';
 
 
-interface Props {
+import * as S from './PageHeading.styled';
+
+interface PageHeadingProps {
   text: string;
   text: string;
-  className?: string;
+  backTo?: string;
+  backText?: string;
 }
 }
 
 
-const PageHeading: React.FC<PropsWithChildren<Props>> = ({
+const PageHeading: React.FC<PropsWithChildren<PageHeadingProps>> = ({
   text,
   text,
-  className,
+  backTo,
+  backText,
   children,
   children,
 }) => {
 }) => {
+  const isBackButtonVisible = backTo && backText;
+
   return (
   return (
-    <div className={className}>
-      <Heading>{text}</Heading>
+    <S.Wrapper>
+      <S.Breadcrumbs>
+        {isBackButtonVisible && <S.BackLink to={backTo}>{backText}</S.BackLink>}
+        <Heading>{text}</Heading>
+      </S.Breadcrumbs>
       <div>{children}</div>
       <div>{children}</div>
-    </div>
+    </S.Wrapper>
   );
   );
 };
 };
 
 
-export default styled(PageHeading)`
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  padding: 16px;
-
-  & > div {
-    display: flex;
-    gap: 16px;
-  }
-`;
+export default PageHeading;

+ 0 - 5
kafka-ui-react-app/src/lib/constants.ts

@@ -1,5 +1,4 @@
 import { ConfigurationParameters } from 'generated-sources';
 import { ConfigurationParameters } from 'generated-sources';
-import { BreadcrumbDefinitions } from 'components/common/Breadcrumb/Breadcrumb';
 
 
 declare global {
 declare global {
   interface Window {
   interface Window {
@@ -59,10 +58,6 @@ export const GIT_REPO_LATEST_RELEASE_LINK =
 export const GIT_TAG = process.env.VITE_TAG;
 export const GIT_TAG = process.env.VITE_TAG;
 export const GIT_COMMIT = process.env.VITE_COMMIT;
 export const GIT_COMMIT = process.env.VITE_COMMIT;
 
 
-export const BREADCRUMB_DEFINITIONS: BreadcrumbDefinitions = {
-  Ksqldb: 'ksqlDB',
-};
-
 export enum AsyncRequestStatus {
 export enum AsyncRequestStatus {
   initial = 'initial',
   initial = 'initial',
   pending = 'pending',
   pending = 'pending',

+ 11 - 2
kafka-ui-react-app/src/theme/theme.ts

@@ -95,8 +95,16 @@ const theme = {
       color: Colors.neutral[20],
       color: Colors.neutral[20],
     },
     },
   },
   },
+  pageHeading: {
+    dividerColor: Colors.neutral[30],
+    backLink: {
+      color: {
+        normal: Colors.brand[70],
+        hover: Colors.brand[60],
+      },
+    },
+  },
   panelColor: Colors.neutral[0],
   panelColor: Colors.neutral[0],
-  breadcrumb: Colors.neutral[30],
   connectEditWarning: Colors.yellow[10],
   connectEditWarning: Colors.yellow[10],
   dropdown: {
   dropdown: {
     backgroundColor: Colors.neutral[0],
     backgroundColor: Colors.neutral[0],
@@ -143,7 +151,7 @@ const theme = {
     },
     },
     variants: {
     variants: {
       1: {
       1: {
-        fontSize: '24px',
+        fontSize: '20px',
         lineHeight: '32px',
         lineHeight: '32px',
       },
       },
       2: {
       2: {
@@ -293,6 +301,7 @@ const theme = {
     border: {
     border: {
       top: Colors.neutral[5],
       top: Colors.neutral[5],
       bottom: Colors.neutral[5],
       bottom: Colors.neutral[5],
+      contrast: Colors.neutral[30],
     },
     },
     overlay: Colors.transparency[10],
     overlay: Colors.transparency[10],
     shadow: Colors.transparency[20],
     shadow: Colors.transparency[20],