[FE] Fix eslint errors
This commit is contained in:
parent
fb67b538c6
commit
cba12d4ff4
44 changed files with 2922 additions and 2400 deletions
|
@ -42,7 +42,8 @@
|
||||||
{ "extensions": [".js", ".jsx", ".ts", ".tsx"] }
|
{ "extensions": [".js", ".jsx", ".ts", ".tsx"] }
|
||||||
],
|
],
|
||||||
"jsx-a11y/label-has-associated-control": "off",
|
"jsx-a11y/label-has-associated-control": "off",
|
||||||
"no-param-reassign": [2, { "props": false }]
|
"no-param-reassign": [2, { "props": false }],
|
||||||
|
"import/prefer-default-export": "off"
|
||||||
},
|
},
|
||||||
"overrides": [
|
"overrides": [
|
||||||
{
|
{
|
||||||
|
|
4501
kafka-ui-react-app/package-lock.json
generated
4501
kafka-ui-react-app/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -4,11 +4,12 @@
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/react-datepicker": "^3.0.2",
|
"@types/react-datepicker": "^3.0.2",
|
||||||
|
"@types/uuid": "^8.3.0",
|
||||||
"bulma": "^0.8.0",
|
"bulma": "^0.8.0",
|
||||||
"bulma-switch": "^2.0.0",
|
"bulma-switch": "^2.0.0",
|
||||||
"classnames": "^2.2.6",
|
"classnames": "^2.2.6",
|
||||||
"date-fns": "^2.14.0",
|
"date-fns": "^2.14.0",
|
||||||
"eslint-import-resolver-typescript": "^2.0.0",
|
"eslint-import-resolver-typescript": "^2.3.0",
|
||||||
"immer": "^6.0.5",
|
"immer": "^6.0.5",
|
||||||
"lodash": "^4.17.15",
|
"lodash": "^4.17.15",
|
||||||
"pretty-ms": "^6.0.1",
|
"pretty-ms": "^6.0.1",
|
||||||
|
@ -24,25 +25,28 @@
|
||||||
"redux-thunk": "^2.3.0",
|
"redux-thunk": "^2.3.0",
|
||||||
"reselect": "^4.0.0",
|
"reselect": "^4.0.0",
|
||||||
"typesafe-actions": "^5.1.0",
|
"typesafe-actions": "^5.1.0",
|
||||||
"use-debounce": "^3.4.3"
|
"use-debounce": "^3.4.3",
|
||||||
|
"uuid": "^8.3.1"
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"*.{js,ts,jsx,tsx}": [
|
"*.{js,ts,jsx,tsx}": [
|
||||||
"eslint -c .eslintrc.json --fix"
|
"eslint -c .eslintrc.json --fix",
|
||||||
|
"git add"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "react-scripts start",
|
"start": "react-scripts start",
|
||||||
"build": "react-scripts build",
|
"build": "react-scripts build",
|
||||||
"lint": "esprint check",
|
"lint": "eslint --ext .tsx,.ts src/",
|
||||||
"lint:fix": "esprint check --fix",
|
"lint:fix": "eslint --ext .tsx,.ts src/ --fix",
|
||||||
"test": "react-scripts test",
|
"test": "react-scripts test",
|
||||||
"eject": "react-scripts eject",
|
"eject": "react-scripts eject",
|
||||||
"mock": "node ./mock/index.js"
|
"mock": "node ./mock/index.js",
|
||||||
|
"tsc": "tsc"
|
||||||
},
|
},
|
||||||
"husky": {
|
"husky": {
|
||||||
"hooks": {
|
"hooks": {
|
||||||
"pre-commit": "yarn tsc --noEmit && lint-staged"
|
"pre-commit": "npm run tsc --noEmit && lint-staged"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
|
@ -74,24 +78,24 @@
|
||||||
"@types/react-router-dom": "^5.1.3",
|
"@types/react-router-dom": "^5.1.3",
|
||||||
"@types/redux": "^3.6.0",
|
"@types/redux": "^3.6.0",
|
||||||
"@types/redux-thunk": "^2.1.0",
|
"@types/redux-thunk": "^2.1.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^2.27.0",
|
"@typescript-eslint/eslint-plugin": "^2.34.0",
|
||||||
"@typescript-eslint/parser": "^2.27.0",
|
"@typescript-eslint/parser": "^2.34.0",
|
||||||
"dotenv": "^8.2.0",
|
"dotenv": "^8.2.0",
|
||||||
"eslint": "^6.8.0",
|
"eslint": "^6.8.0",
|
||||||
"eslint-config-airbnb": "^18.1.0",
|
"eslint-config-airbnb": "^18.2.1",
|
||||||
"eslint-config-prettier": "^6.10.1",
|
"eslint-config-prettier": "^6.15.0",
|
||||||
"eslint-plugin-import": "^2.20.2",
|
"eslint-plugin-import": "^2.22.1",
|
||||||
"eslint-plugin-jsx-a11y": "^6.2.3",
|
"eslint-plugin-jsx-a11y": "^6.4.1",
|
||||||
"eslint-plugin-prettier": "^3.1.2",
|
"eslint-plugin-prettier": "^3.1.4",
|
||||||
"eslint-plugin-react": "^7.19.0",
|
"eslint-plugin-react": "^7.21.5",
|
||||||
"eslint-plugin-react-hooks": "^2.5.1",
|
"eslint-plugin-react-hooks": "^2.5.1",
|
||||||
"esprint": "^0.6.0",
|
"esprint": "^0.6.0",
|
||||||
"husky": "^4.2.5",
|
"husky": "^4.3.0",
|
||||||
"json-server": "^0.15.1",
|
"json-server": "^0.15.1",
|
||||||
"lint-staged": ">=10",
|
"lint-staged": "^10.5.1",
|
||||||
"node-sass": "^4.13.1",
|
"node-sass": "^4.13.1",
|
||||||
"prettier": "^2.0.4",
|
"prettier": "^2.1.2",
|
||||||
"react-scripts": "3.4.0",
|
"react-scripts": "3.4.4",
|
||||||
"typescript": "~3.7.4"
|
"typescript": "~3.7.4"
|
||||||
},
|
},
|
||||||
"proxy": "http://localhost:8080"
|
"proxy": "http://localhost:8080"
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import {
|
import { fetchClustersList } from 'redux/actions';
|
||||||
fetchClustersList,
|
|
||||||
} from 'redux/actions';
|
|
||||||
import App from './App';
|
|
||||||
import { getIsClusterListFetched } from 'redux/reducers/clusters/selectors';
|
import { getIsClusterListFetched } from 'redux/reducers/clusters/selectors';
|
||||||
import { RootState } from 'redux/interfaces';
|
import { RootState } from 'redux/interfaces';
|
||||||
|
import App from './App';
|
||||||
|
|
||||||
const mapStateToProps = (state: RootState) => ({
|
const mapStateToProps = (state: RootState) => ({
|
||||||
isClusterListFetched: getIsClusterListFetched(state),
|
isClusterListFetched: getIsClusterListFetched(state),
|
||||||
|
@ -12,6 +10,6 @@ const mapStateToProps = (state: RootState) => ({
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
fetchClustersList,
|
fetchClustersList,
|
||||||
}
|
};
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(App);
|
export default connect(mapStateToProps, mapDispatchToProps)(App);
|
||||||
|
|
|
@ -27,15 +27,14 @@ const Topics: React.FC<Props> = ({
|
||||||
fetchBrokers,
|
fetchBrokers,
|
||||||
fetchBrokerMetrics,
|
fetchBrokerMetrics,
|
||||||
}) => {
|
}) => {
|
||||||
React.useEffect(
|
React.useEffect(() => {
|
||||||
() => {
|
fetchBrokers(clusterName);
|
||||||
fetchBrokers(clusterName);
|
fetchBrokerMetrics(clusterName);
|
||||||
fetchBrokerMetrics(clusterName);
|
}, [fetchBrokers, fetchBrokerMetrics, clusterName]);
|
||||||
},
|
|
||||||
[fetchBrokers, fetchBrokerMetrics, clusterName],
|
|
||||||
);
|
|
||||||
|
|
||||||
useInterval(() => { fetchBrokerMetrics(clusterName); }, 5000);
|
useInterval(() => {
|
||||||
|
fetchBrokerMetrics(clusterName);
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
const zkOnline = zooKeeperStatus === ZooKeeperStatus.online;
|
const zkOnline = zooKeeperStatus === ZooKeeperStatus.online;
|
||||||
|
|
||||||
|
@ -44,12 +43,8 @@ const Topics: React.FC<Props> = ({
|
||||||
<Breadcrumb>Brokers overview</Breadcrumb>
|
<Breadcrumb>Brokers overview</Breadcrumb>
|
||||||
|
|
||||||
<MetricsWrapper title="Uptime">
|
<MetricsWrapper title="Uptime">
|
||||||
<Indicator label="Total Brokers">
|
<Indicator label="Total Brokers">{brokerCount}</Indicator>
|
||||||
{brokerCount}
|
<Indicator label="Active Controllers">{activeControllers}</Indicator>
|
||||||
</Indicator>
|
|
||||||
<Indicator label="Active Controllers">
|
|
||||||
{activeControllers}
|
|
||||||
</Indicator>
|
|
||||||
<Indicator label="Zookeeper Status">
|
<Indicator label="Zookeeper Status">
|
||||||
<span className={cx('tag', zkOnline ? 'is-primary' : 'is-danger')}>
|
<span className={cx('tag', zkOnline ? 'is-primary' : 'is-danger')}>
|
||||||
{zkOnline ? 'Online' : 'Offline'}
|
{zkOnline ? 'Online' : 'Offline'}
|
||||||
|
@ -59,17 +54,21 @@ const Topics: React.FC<Props> = ({
|
||||||
|
|
||||||
<MetricsWrapper title="Partitions">
|
<MetricsWrapper title="Partitions">
|
||||||
<Indicator label="Online">
|
<Indicator label="Online">
|
||||||
<span className={cx({'has-text-danger': offlinePartitionCount !== 0})}>
|
<span
|
||||||
|
className={cx({ 'has-text-danger': offlinePartitionCount !== 0 })}
|
||||||
|
>
|
||||||
{onlinePartitionCount}
|
{onlinePartitionCount}
|
||||||
</span>
|
</span>
|
||||||
<span className="subtitle has-text-weight-light"> of {onlinePartitionCount + offlinePartitionCount}</span>
|
<span className="subtitle has-text-weight-light">
|
||||||
|
{' '}
|
||||||
|
of
|
||||||
|
{onlinePartitionCount + offlinePartitionCount}
|
||||||
|
</span>
|
||||||
</Indicator>
|
</Indicator>
|
||||||
<Indicator label="URP" title="Under replicated partitions">
|
<Indicator label="URP" title="Under replicated partitions">
|
||||||
{underReplicatedPartitionCount}
|
{underReplicatedPartitionCount}
|
||||||
</Indicator>
|
</Indicator>
|
||||||
<Indicator label="In Sync Replicas">
|
<Indicator label="In Sync Replicas">{inSyncReplicasCount}</Indicator>
|
||||||
{inSyncReplicasCount}
|
|
||||||
</Indicator>
|
|
||||||
<Indicator label="Out of Sync Replicas">
|
<Indicator label="Out of Sync Replicas">
|
||||||
{outOfSyncReplicasCount}
|
{outOfSyncReplicasCount}
|
||||||
</Indicator>
|
</Indicator>
|
||||||
|
|
|
@ -1,20 +1,24 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import {
|
import { fetchBrokers, fetchBrokerMetrics } from 'redux/actions';
|
||||||
fetchBrokers,
|
|
||||||
fetchBrokerMetrics,
|
|
||||||
} from 'redux/actions';
|
|
||||||
import Brokers from './Brokers';
|
|
||||||
import * as brokerSelectors from 'redux/reducers/brokers/selectors';
|
import * as brokerSelectors from 'redux/reducers/brokers/selectors';
|
||||||
import { RootState, ClusterName } from 'redux/interfaces';
|
import { RootState, ClusterName } from 'redux/interfaces';
|
||||||
import { RouteComponentProps } from 'react-router-dom';
|
import { RouteComponentProps } from 'react-router-dom';
|
||||||
|
import Brokers from './Brokers';
|
||||||
|
|
||||||
interface RouteProps {
|
interface RouteProps {
|
||||||
clusterName: ClusterName;
|
clusterName: ClusterName;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface OwnProps extends RouteComponentProps<RouteProps> { }
|
type OwnProps = RouteComponentProps<RouteProps>;
|
||||||
|
|
||||||
const mapStateToProps = (state: RootState, { match: { params: { clusterName } }}: OwnProps) => ({
|
const mapStateToProps = (
|
||||||
|
state: RootState,
|
||||||
|
{
|
||||||
|
match: {
|
||||||
|
params: { clusterName },
|
||||||
|
},
|
||||||
|
}: OwnProps
|
||||||
|
) => ({
|
||||||
isFetched: brokerSelectors.getIsBrokerListFetched(state),
|
isFetched: brokerSelectors.getIsBrokerListFetched(state),
|
||||||
clusterName,
|
clusterName,
|
||||||
brokerCount: brokerSelectors.getBrokerCount(state),
|
brokerCount: brokerSelectors.getBrokerCount(state),
|
||||||
|
@ -24,12 +28,15 @@ const mapStateToProps = (state: RootState, { match: { params: { clusterName } }}
|
||||||
offlinePartitionCount: brokerSelectors.getOfflinePartitionCount(state),
|
offlinePartitionCount: brokerSelectors.getOfflinePartitionCount(state),
|
||||||
inSyncReplicasCount: brokerSelectors.getInSyncReplicasCount(state),
|
inSyncReplicasCount: brokerSelectors.getInSyncReplicasCount(state),
|
||||||
outOfSyncReplicasCount: brokerSelectors.getOutOfSyncReplicasCount(state),
|
outOfSyncReplicasCount: brokerSelectors.getOutOfSyncReplicasCount(state),
|
||||||
underReplicatedPartitionCount: brokerSelectors.getUnderReplicatedPartitionCount(state)
|
underReplicatedPartitionCount: brokerSelectors.getUnderReplicatedPartitionCount(
|
||||||
|
state
|
||||||
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
fetchBrokers: (clusterName: ClusterName) => fetchBrokers(clusterName),
|
fetchBrokers: (clusterName: ClusterName) => fetchBrokers(clusterName),
|
||||||
fetchBrokerMetrics: (clusterName: ClusterName) => fetchBrokerMetrics(clusterName),
|
fetchBrokerMetrics: (clusterName: ClusterName) =>
|
||||||
|
fetchBrokerMetrics(clusterName),
|
||||||
};
|
};
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(Brokers);
|
export default connect(mapStateToProps, mapDispatchToProps)(Brokers);
|
||||||
|
|
|
@ -5,20 +5,27 @@ import { RouteComponentProps } from 'react-router-dom';
|
||||||
import ConsumerGroups from './ConsumerGroups';
|
import ConsumerGroups from './ConsumerGroups';
|
||||||
import { getIsConsumerGroupsListFetched } from '../../redux/reducers/consumerGroups/selectors';
|
import { getIsConsumerGroupsListFetched } from '../../redux/reducers/consumerGroups/selectors';
|
||||||
|
|
||||||
|
|
||||||
interface RouteProps {
|
interface RouteProps {
|
||||||
clusterName: ClusterName;
|
clusterName: ClusterName;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface OwnProps extends RouteComponentProps<RouteProps> { }
|
type OwnProps = RouteComponentProps<RouteProps>;
|
||||||
|
|
||||||
const mapStateToProps = (state: RootState, { match: { params: { clusterName } }}: OwnProps) => ({
|
const mapStateToProps = (
|
||||||
|
state: RootState,
|
||||||
|
{
|
||||||
|
match: {
|
||||||
|
params: { clusterName },
|
||||||
|
},
|
||||||
|
}: OwnProps
|
||||||
|
) => ({
|
||||||
isFetched: getIsConsumerGroupsListFetched(state),
|
isFetched: getIsConsumerGroupsListFetched(state),
|
||||||
clusterName,
|
clusterName,
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
fetchConsumerGroupsList: (clusterName: ClusterName) => fetchConsumerGroupsList(clusterName),
|
fetchConsumerGroupsList: (clusterName: ClusterName) =>
|
||||||
|
fetchConsumerGroupsList(clusterName),
|
||||||
};
|
};
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(ConsumerGroups);
|
export default connect(mapStateToProps, mapDispatchToProps)(ConsumerGroups);
|
||||||
|
|
|
@ -1,27 +1,40 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import Details from './Details';
|
import { ClusterName, RootState } from 'redux/interfaces';
|
||||||
import {ClusterName, RootState} from 'redux/interfaces';
|
|
||||||
import { withRouter, RouteComponentProps } from 'react-router-dom';
|
import { withRouter, RouteComponentProps } from 'react-router-dom';
|
||||||
import { getIsConsumerGroupDetailsFetched, getConsumerGroupByID } from 'redux/reducers/consumerGroups/selectors';
|
import {
|
||||||
|
getIsConsumerGroupDetailsFetched,
|
||||||
|
getConsumerGroupByID,
|
||||||
|
} from 'redux/reducers/consumerGroups/selectors';
|
||||||
import { ConsumerGroupID } from 'redux/interfaces/consumerGroup';
|
import { ConsumerGroupID } from 'redux/interfaces/consumerGroup';
|
||||||
import { fetchConsumerGroupDetails } from 'redux/actions/thunks';
|
import { fetchConsumerGroupDetails } from 'redux/actions/thunks';
|
||||||
|
import Details from './Details';
|
||||||
|
|
||||||
interface RouteProps {
|
interface RouteProps {
|
||||||
clusterName: ClusterName;
|
clusterName: ClusterName;
|
||||||
consumerGroupID: string;
|
consumerGroupID: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface OwnProps extends RouteComponentProps<RouteProps> { }
|
type OwnProps = RouteComponentProps<RouteProps>;
|
||||||
|
|
||||||
const mapStateToProps = (state: RootState, { match: { params: { consumerGroupID, clusterName } } }: OwnProps) => ({
|
const mapStateToProps = (
|
||||||
|
state: RootState,
|
||||||
|
{
|
||||||
|
match: {
|
||||||
|
params: { consumerGroupID, clusterName },
|
||||||
|
},
|
||||||
|
}: OwnProps
|
||||||
|
) => ({
|
||||||
clusterName,
|
clusterName,
|
||||||
consumerGroupID,
|
consumerGroupID,
|
||||||
isFetched: getIsConsumerGroupDetailsFetched(state),
|
isFetched: getIsConsumerGroupDetailsFetched(state),
|
||||||
...getConsumerGroupByID(state, consumerGroupID)
|
...getConsumerGroupByID(state, consumerGroupID),
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
fetchConsumerGroupDetails: (clusterName: ClusterName, consumerGroupID: ConsumerGroupID) => fetchConsumerGroupDetails(clusterName, consumerGroupID),
|
fetchConsumerGroupDetails: (
|
||||||
|
clusterName: ClusterName,
|
||||||
|
consumerGroupID: ConsumerGroupID
|
||||||
|
) => fetchConsumerGroupDetails(clusterName, consumerGroupID),
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withRouter(
|
export default withRouter(
|
||||||
|
|
|
@ -1,20 +1,25 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import {ClusterName, RootState} from 'redux/interfaces';
|
import { ClusterName, RootState } from 'redux/interfaces';
|
||||||
import { getConsumerGroupsList } from 'redux/reducers/consumerGroups/selectors';
|
import { getConsumerGroupsList } from 'redux/reducers/consumerGroups/selectors';
|
||||||
import List from './List';
|
|
||||||
import { withRouter, RouteComponentProps } from 'react-router-dom';
|
import { withRouter, RouteComponentProps } from 'react-router-dom';
|
||||||
|
import List from './List';
|
||||||
|
|
||||||
interface RouteProps {
|
interface RouteProps {
|
||||||
clusterName: ClusterName;
|
clusterName: ClusterName;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface OwnProps extends RouteComponentProps<RouteProps> { }
|
type OwnProps = RouteComponentProps<RouteProps>;
|
||||||
|
|
||||||
const mapStateToProps = (state: RootState, { match: { params: { clusterName } } }: OwnProps) => ({
|
const mapStateToProps = (
|
||||||
|
state: RootState,
|
||||||
|
{
|
||||||
|
match: {
|
||||||
|
params: { clusterName },
|
||||||
|
},
|
||||||
|
}: OwnProps
|
||||||
|
) => ({
|
||||||
clusterName,
|
clusterName,
|
||||||
consumerGroups: getConsumerGroupsList(state)
|
consumerGroups: getConsumerGroupsList(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default withRouter(
|
export default withRouter(connect(mapStateToProps)(List));
|
||||||
connect(mapStateToProps)(List)
|
|
||||||
);
|
|
||||||
|
|
|
@ -4,23 +4,28 @@ import formatBytes from 'lib/utils/formatBytes';
|
||||||
import { NavLink } from 'react-router-dom';
|
import { NavLink } from 'react-router-dom';
|
||||||
import { clusterBrokersPath } from 'lib/paths';
|
import { clusterBrokersPath } from 'lib/paths';
|
||||||
|
|
||||||
const ClusterWidget: React.FC<Cluster> = ({
|
interface ClusterWidgetProps {
|
||||||
name,
|
cluster: Cluster;
|
||||||
status,
|
}
|
||||||
topicCount,
|
|
||||||
brokerCount,
|
const ClusterWidget: React.FC<ClusterWidgetProps> = ({
|
||||||
bytesInPerSec,
|
cluster: {
|
||||||
bytesOutPerSec,
|
name,
|
||||||
onlinePartitionCount,
|
status,
|
||||||
|
topicCount,
|
||||||
|
brokerCount,
|
||||||
|
bytesInPerSec,
|
||||||
|
bytesOutPerSec,
|
||||||
|
onlinePartitionCount,
|
||||||
|
},
|
||||||
}) => (
|
}) => (
|
||||||
<NavLink to={clusterBrokersPath(name)} className="column is-full-modile is-6">
|
<NavLink to={clusterBrokersPath(name)} className="column is-full-modile is-6">
|
||||||
<div className="box is-hoverable">
|
<div className="box is-hoverable">
|
||||||
<div
|
<div className="title is-6 has-text-overflow-ellipsis" title={name}>
|
||||||
className="title is-6 has-text-overflow-ellipsis"
|
|
||||||
title={name}
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
className={`tag has-margin-right ${status === ClusterStatus.Online ? 'is-primary' : 'is-danger'}`}
|
className={`tag has-margin-right ${
|
||||||
|
status === ClusterStatus.Online ? 'is-primary' : 'is-danger'
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
{status}
|
{status}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { chunk } from 'lodash';
|
import { chunk } from 'lodash';
|
||||||
|
import { v4 } from 'uuid';
|
||||||
import { Cluster } from 'redux/interfaces';
|
import { Cluster } from 'redux/interfaces';
|
||||||
import MetricsWrapper from 'components/common/Dashboard/MetricsWrapper';
|
import MetricsWrapper from 'components/common/Dashboard/MetricsWrapper';
|
||||||
import Indicator from 'components/common/Dashboard/Indicator';
|
import Indicator from 'components/common/Dashboard/Indicator';
|
||||||
|
@ -19,35 +20,27 @@ const ClustersWidget: React.FC<Props> = ({
|
||||||
const [showOfflineOnly, setShowOfflineOnly] = React.useState<boolean>(false);
|
const [showOfflineOnly, setShowOfflineOnly] = React.useState<boolean>(false);
|
||||||
|
|
||||||
const clusterList: Array<Cluster[]> = React.useMemo(() => {
|
const clusterList: Array<Cluster[]> = React.useMemo(() => {
|
||||||
let list = clusters;
|
let list = clusters;
|
||||||
|
|
||||||
if (showOfflineOnly) {
|
if (showOfflineOnly) {
|
||||||
list = offlineClusters;
|
list = offlineClusters;
|
||||||
}
|
}
|
||||||
|
|
||||||
return chunk(list, 2);
|
return chunk(list, 2);
|
||||||
},
|
}, [clusters, offlineClusters, showOfflineOnly]);
|
||||||
[clusters, offlineClusters, showOfflineOnly],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleSwitch = () => setShowOfflineOnly(!showOfflineOnly);
|
const handleSwitch = () => setShowOfflineOnly(!showOfflineOnly);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h5 className="title is-5">
|
<h5 className="title is-5">Clusters</h5>
|
||||||
Clusters
|
|
||||||
</h5>
|
|
||||||
|
|
||||||
<MetricsWrapper>
|
<MetricsWrapper>
|
||||||
<Indicator label="Online Clusters" >
|
<Indicator label="Online Clusters">
|
||||||
<span className="tag is-primary">
|
<span className="tag is-primary">{onlineClusters.length}</span>
|
||||||
{onlineClusters.length}
|
|
||||||
</span>
|
|
||||||
</Indicator>
|
</Indicator>
|
||||||
<Indicator label="Offline Clusters">
|
<Indicator label="Offline Clusters">
|
||||||
<span className="tag is-danger">
|
<span className="tag is-danger">{offlineClusters.length}</span>
|
||||||
{offlineClusters.length}
|
|
||||||
</span>
|
|
||||||
</Indicator>
|
</Indicator>
|
||||||
<Indicator label="Hide online clusters">
|
<Indicator label="Hide online clusters">
|
||||||
<input
|
<input
|
||||||
|
@ -58,20 +51,19 @@ const ClustersWidget: React.FC<Props> = ({
|
||||||
checked={showOfflineOnly}
|
checked={showOfflineOnly}
|
||||||
onChange={handleSwitch}
|
onChange={handleSwitch}
|
||||||
/>
|
/>
|
||||||
<label htmlFor="switchRoundedDefault">
|
<label htmlFor="switchRoundedDefault" />
|
||||||
</label>
|
|
||||||
</Indicator>
|
</Indicator>
|
||||||
</MetricsWrapper>
|
</MetricsWrapper>
|
||||||
|
|
||||||
{clusterList.map((chunk, idx) => (
|
{clusterList.map((chunkItem) => (
|
||||||
<div className="columns" key={`dashboard-cluster-list-row-key-${idx}`}>
|
<div className="columns" key={v4()}>
|
||||||
{chunk.map((cluster, idx) => (
|
{chunkItem.map((cluster) => (
|
||||||
<ClusterWidget {...cluster} key={`dashboard-cluster-list-item-key-${idx}`}/>
|
<ClusterWidget cluster={cluster} key={cluster.id} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ClustersWidget;
|
export default ClustersWidget;
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import ClustersWidget from './ClustersWidget';
|
|
||||||
import {
|
import {
|
||||||
getClusterList,
|
getClusterList,
|
||||||
getOnlineClusters,
|
getOnlineClusters,
|
||||||
getOfflineClusters,
|
getOfflineClusters,
|
||||||
} from 'redux/reducers/clusters/selectors';
|
} from 'redux/reducers/clusters/selectors';
|
||||||
import { RootState } from 'redux/interfaces';
|
import { RootState } from 'redux/interfaces';
|
||||||
|
import ClustersWidget from './ClustersWidget';
|
||||||
|
|
||||||
const mapStateToProps = (state: RootState) => ({
|
const mapStateToProps = (state: RootState) => ({
|
||||||
clusters: getClusterList(state),
|
clusters: getClusterList(state),
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import Nav from './Nav';
|
import {
|
||||||
import { getIsClusterListFetched, getClusterList } from 'redux/reducers/clusters/selectors';
|
getIsClusterListFetched,
|
||||||
|
getClusterList,
|
||||||
|
} from 'redux/reducers/clusters/selectors';
|
||||||
import { RootState } from 'redux/interfaces';
|
import { RootState } from 'redux/interfaces';
|
||||||
|
import Nav from './Nav';
|
||||||
|
|
||||||
const mapStateToProps = (state: RootState) => ({
|
const mapStateToProps = (state: RootState) => ({
|
||||||
isClusterListFetched: getIsClusterListFetched(state),
|
isClusterListFetched: getIsClusterListFetched(state),
|
||||||
|
|
|
@ -1,20 +1,25 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import Details from './Details';
|
import { ClusterName, RootState } from 'redux/interfaces';
|
||||||
import {ClusterName, RootState} from 'redux/interfaces';
|
|
||||||
import { withRouter, RouteComponentProps } from 'react-router-dom';
|
import { withRouter, RouteComponentProps } from 'react-router-dom';
|
||||||
|
import Details from './Details';
|
||||||
|
|
||||||
interface RouteProps {
|
interface RouteProps {
|
||||||
clusterName: ClusterName;
|
clusterName: ClusterName;
|
||||||
topicName: string;
|
topicName: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface OwnProps extends RouteComponentProps<RouteProps> { }
|
type OwnProps = RouteComponentProps<RouteProps>;
|
||||||
|
|
||||||
const mapStateToProps = (state: RootState, { match: { params: { topicName, clusterName } } }: OwnProps) => ({
|
const mapStateToProps = (
|
||||||
|
state: RootState,
|
||||||
|
{
|
||||||
|
match: {
|
||||||
|
params: { topicName, clusterName },
|
||||||
|
},
|
||||||
|
}: OwnProps
|
||||||
|
) => ({
|
||||||
clusterName,
|
clusterName,
|
||||||
topicName,
|
topicName,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default withRouter(
|
export default withRouter(connect(mapStateToProps)(Details));
|
||||||
connect(mapStateToProps)(Details)
|
|
||||||
);
|
|
||||||
|
|
|
@ -177,7 +177,7 @@ const Messages: React.FC<Props> = ({
|
||||||
}, [clusterName, topicName, queryParams]);
|
}, [clusterName, topicName, queryParams]);
|
||||||
|
|
||||||
const getTimestampDate = (timestamp: string) => {
|
const getTimestampDate = (timestamp: string) => {
|
||||||
if (!Date.parse(timestamp)) return;
|
if (!Date.parse(timestamp)) return null;
|
||||||
return format(Date.parse(timestamp), 'yyyy-MM-dd HH:mm:ss');
|
return format(Date.parse(timestamp), 'yyyy-MM-dd HH:mm:ss');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -359,7 +359,7 @@ const Messages: React.FC<Props> = ({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="columns">
|
<div className="columns">
|
||||||
<div className="column is-full" style={{textAlign: "right"}}>
|
<div className="column is-full" style={{ textAlign: 'right' }}>
|
||||||
<input
|
<input
|
||||||
type="submit"
|
type="submit"
|
||||||
className="button is-primary"
|
className="button is-primary"
|
||||||
|
|
|
@ -1,20 +1,28 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import {
|
import { fetchTopicDetails } from 'redux/actions';
|
||||||
fetchTopicDetails,
|
|
||||||
} from 'redux/actions';
|
|
||||||
import Overview from './Overview';
|
|
||||||
import { RootState, TopicName, ClusterName } from 'redux/interfaces';
|
import { RootState, TopicName, ClusterName } from 'redux/interfaces';
|
||||||
import { getTopicByName, getIsTopicDetailsFetched } from 'redux/reducers/topics/selectors';
|
import {
|
||||||
|
getTopicByName,
|
||||||
|
getIsTopicDetailsFetched,
|
||||||
|
} from 'redux/reducers/topics/selectors';
|
||||||
import { withRouter, RouteComponentProps } from 'react-router-dom';
|
import { withRouter, RouteComponentProps } from 'react-router-dom';
|
||||||
|
import Overview from './Overview';
|
||||||
|
|
||||||
interface RouteProps {
|
interface RouteProps {
|
||||||
clusterName: ClusterName;
|
clusterName: ClusterName;
|
||||||
topicName: TopicName;
|
topicName: TopicName;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface OwnProps extends RouteComponentProps<RouteProps> { }
|
type OwnProps = RouteComponentProps<RouteProps>;
|
||||||
|
|
||||||
const mapStateToProps = (state: RootState, { match: { params: { topicName, clusterName } } }: OwnProps) => ({
|
const mapStateToProps = (
|
||||||
|
state: RootState,
|
||||||
|
{
|
||||||
|
match: {
|
||||||
|
params: { topicName, clusterName },
|
||||||
|
},
|
||||||
|
}: OwnProps
|
||||||
|
) => ({
|
||||||
clusterName,
|
clusterName,
|
||||||
topicName,
|
topicName,
|
||||||
isFetched: getIsTopicDetailsFetched(state),
|
isFetched: getIsTopicDetailsFetched(state),
|
||||||
|
@ -22,7 +30,8 @@ const mapStateToProps = (state: RootState, { match: { params: { topicName, clust
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
fetchTopicDetails: (clusterName: ClusterName, topicName: TopicName) => fetchTopicDetails(clusterName, topicName),
|
fetchTopicDetails: (clusterName: ClusterName, topicName: TopicName) =>
|
||||||
|
fetchTopicDetails(clusterName, topicName),
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withRouter(
|
export default withRouter(
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { v4 } from 'uuid';
|
||||||
import { ClusterName, TopicName, TopicConfig } from 'redux/interfaces';
|
import { ClusterName, TopicName, TopicConfig } from 'redux/interfaces';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
@ -9,29 +10,24 @@ interface Props {
|
||||||
fetchTopicConfig: (clusterName: ClusterName, topicName: TopicName) => void;
|
fetchTopicConfig: (clusterName: ClusterName, topicName: TopicName) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ConfigListItem: React.FC<TopicConfig> = ({
|
interface ListItemProps {
|
||||||
name,
|
config: TopicConfig;
|
||||||
value,
|
}
|
||||||
defaultValue,
|
|
||||||
|
const ConfigListItem: React.FC<ListItemProps> = ({
|
||||||
|
config: { name, value, defaultValue },
|
||||||
}) => {
|
}) => {
|
||||||
const hasCustomValue = value !== defaultValue;
|
const hasCustomValue = value !== defaultValue;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr>
|
<tr>
|
||||||
<td className={hasCustomValue ? 'has-text-weight-bold' : ''}>
|
<td className={hasCustomValue ? 'has-text-weight-bold' : ''}>{name}</td>
|
||||||
{name}
|
<td className={hasCustomValue ? 'has-text-weight-bold' : ''}>{value}</td>
|
||||||
</td>
|
<td className="has-text-grey" title="Default Value">
|
||||||
<td className={hasCustomValue ? 'has-text-weight-bold' : ''}>
|
|
||||||
{value}
|
|
||||||
</td>
|
|
||||||
<td
|
|
||||||
className="has-text-grey"
|
|
||||||
title="Default Value"
|
|
||||||
>
|
|
||||||
{hasCustomValue && defaultValue}
|
{hasCustomValue && defaultValue}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
)
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const Sertings: React.FC<Props> = ({
|
const Sertings: React.FC<Props> = ({
|
||||||
|
@ -41,10 +37,9 @@ const Sertings: React.FC<Props> = ({
|
||||||
fetchTopicConfig,
|
fetchTopicConfig,
|
||||||
config,
|
config,
|
||||||
}) => {
|
}) => {
|
||||||
React.useEffect(
|
React.useEffect(() => {
|
||||||
() => { fetchTopicConfig(clusterName, topicName); },
|
fetchTopicConfig(clusterName, topicName);
|
||||||
[fetchTopicConfig, clusterName, topicName],
|
}, [fetchTopicConfig, clusterName, topicName]);
|
||||||
);
|
|
||||||
|
|
||||||
if (!isFetched || !config) {
|
if (!isFetched || !config) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -61,7 +56,9 @@ const Sertings: React.FC<Props> = ({
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{config.map((item, index) => <ConfigListItem key={`config-list-item-key-${index}`} {...item} />)}
|
{config.map((item) => (
|
||||||
|
<ConfigListItem key={v4()} config={item} />
|
||||||
|
))}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,24 +1,28 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { RootState, ClusterName, TopicName } from 'redux/interfaces';
|
import { RootState, ClusterName, TopicName } from 'redux/interfaces';
|
||||||
import { withRouter, RouteComponentProps } from 'react-router-dom';
|
import { withRouter, RouteComponentProps } from 'react-router-dom';
|
||||||
import {
|
import { fetchTopicConfig } from 'redux/actions';
|
||||||
fetchTopicConfig,
|
|
||||||
} from 'redux/actions';
|
|
||||||
import Settings from './Settings';
|
|
||||||
import {
|
import {
|
||||||
getTopicConfig,
|
getTopicConfig,
|
||||||
getTopicConfigFetched,
|
getTopicConfigFetched,
|
||||||
} from 'redux/reducers/topics/selectors';
|
} from 'redux/reducers/topics/selectors';
|
||||||
|
import Settings from './Settings';
|
||||||
|
|
||||||
interface RouteProps {
|
interface RouteProps {
|
||||||
clusterName: ClusterName;
|
clusterName: ClusterName;
|
||||||
topicName: TopicName;
|
topicName: TopicName;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface OwnProps extends RouteComponentProps<RouteProps> { }
|
type OwnProps = RouteComponentProps<RouteProps>;
|
||||||
|
|
||||||
const mapStateToProps = (state: RootState, { match: { params: { topicName, clusterName } } }: OwnProps) => ({
|
const mapStateToProps = (
|
||||||
|
state: RootState,
|
||||||
|
{
|
||||||
|
match: {
|
||||||
|
params: { topicName, clusterName },
|
||||||
|
},
|
||||||
|
}: OwnProps
|
||||||
|
) => ({
|
||||||
clusterName,
|
clusterName,
|
||||||
topicName,
|
topicName,
|
||||||
config: getTopicConfig(state, topicName),
|
config: getTopicConfig(state, topicName),
|
||||||
|
@ -26,7 +30,8 @@ const mapStateToProps = (state: RootState, { match: { params: { topicName, clust
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
fetchTopicConfig: (clusterName: ClusterName, topicName: TopicName) => fetchTopicConfig(clusterName, topicName),
|
fetchTopicConfig: (clusterName: ClusterName, topicName: TopicName) =>
|
||||||
|
fetchTopicConfig(clusterName, topicName),
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withRouter(
|
export default withRouter(
|
||||||
|
|
|
@ -1,21 +1,18 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { v4 } from 'uuid';
|
||||||
import { TopicWithDetailedInfo, ClusterName } from 'redux/interfaces';
|
import { TopicWithDetailedInfo, ClusterName } from 'redux/interfaces';
|
||||||
import ListItem from './ListItem';
|
|
||||||
import Breadcrumb from 'components/common/Breadcrumb/Breadcrumb';
|
import Breadcrumb from 'components/common/Breadcrumb/Breadcrumb';
|
||||||
import { NavLink } from 'react-router-dom';
|
import { NavLink } from 'react-router-dom';
|
||||||
import { clusterTopicNewPath } from 'lib/paths';
|
import { clusterTopicNewPath } from 'lib/paths';
|
||||||
|
import ListItem from './ListItem';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
clusterName: ClusterName;
|
clusterName: ClusterName;
|
||||||
topics: (TopicWithDetailedInfo)[];
|
topics: TopicWithDetailedInfo[];
|
||||||
externalTopics: (TopicWithDetailedInfo)[];
|
externalTopics: TopicWithDetailedInfo[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const List: React.FC<Props> = ({
|
const List: React.FC<Props> = ({ clusterName, topics, externalTopics }) => {
|
||||||
clusterName,
|
|
||||||
topics,
|
|
||||||
externalTopics,
|
|
||||||
}) => {
|
|
||||||
const [showInternal, setShowInternal] = React.useState<boolean>(true);
|
const [showInternal, setShowInternal] = React.useState<boolean>(true);
|
||||||
|
|
||||||
const handleSwitch = () => setShowInternal(!showInternal);
|
const handleSwitch = () => setShowInternal(!showInternal);
|
||||||
|
@ -38,9 +35,7 @@ const List: React.FC<Props> = ({
|
||||||
checked={showInternal}
|
checked={showInternal}
|
||||||
onChange={handleSwitch}
|
onChange={handleSwitch}
|
||||||
/>
|
/>
|
||||||
<label htmlFor="switchRoundedDefault">
|
<label htmlFor="switchRoundedDefault">Show Internal Topics</label>
|
||||||
Show Internal Topics
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="level-item level-right">
|
<div className="level-item level-right">
|
||||||
|
@ -64,11 +59,8 @@ const List: React.FC<Props> = ({
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{items.map((topic, index) => (
|
{items.map((topic) => (
|
||||||
<ListItem
|
<ListItem key={v4()} topic={topic} />
|
||||||
key={`topic-list-item-key-${index}`}
|
|
||||||
{...topic}
|
|
||||||
/>
|
|
||||||
))}
|
))}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
@ -1,21 +1,29 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import {ClusterName, RootState} from 'redux/interfaces';
|
import { ClusterName, RootState } from 'redux/interfaces';
|
||||||
import { getTopicList, getExternalTopicList } from 'redux/reducers/topics/selectors';
|
import {
|
||||||
import List from './List';
|
getTopicList,
|
||||||
|
getExternalTopicList,
|
||||||
|
} from 'redux/reducers/topics/selectors';
|
||||||
import { withRouter, RouteComponentProps } from 'react-router-dom';
|
import { withRouter, RouteComponentProps } from 'react-router-dom';
|
||||||
|
import List from './List';
|
||||||
|
|
||||||
interface RouteProps {
|
interface RouteProps {
|
||||||
clusterName: ClusterName;
|
clusterName: ClusterName;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface OwnProps extends RouteComponentProps<RouteProps> { }
|
type OwnProps = RouteComponentProps<RouteProps>;
|
||||||
|
|
||||||
const mapStateToProps = (state: RootState, { match: { params: { clusterName } } }: OwnProps) => ({
|
const mapStateToProps = (
|
||||||
|
state: RootState,
|
||||||
|
{
|
||||||
|
match: {
|
||||||
|
params: { clusterName },
|
||||||
|
},
|
||||||
|
}: OwnProps
|
||||||
|
) => ({
|
||||||
clusterName,
|
clusterName,
|
||||||
topics: getTopicList(state),
|
topics: getTopicList(state),
|
||||||
externalTopics: getExternalTopicList(state),
|
externalTopics: getExternalTopicList(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default withRouter(
|
export default withRouter(connect(mapStateToProps)(List));
|
||||||
connect(mapStateToProps)(List)
|
|
||||||
);
|
|
||||||
|
|
|
@ -3,10 +3,12 @@ import cx from 'classnames';
|
||||||
import { NavLink } from 'react-router-dom';
|
import { NavLink } from 'react-router-dom';
|
||||||
import { TopicWithDetailedInfo } from 'redux/interfaces';
|
import { TopicWithDetailedInfo } from 'redux/interfaces';
|
||||||
|
|
||||||
const ListItem: React.FC<TopicWithDetailedInfo> = ({
|
interface ListItemProps {
|
||||||
name,
|
topic: TopicWithDetailedInfo;
|
||||||
internal,
|
}
|
||||||
partitions,
|
|
||||||
|
const ListItem: React.FC<ListItemProps> = ({
|
||||||
|
topic: { name, internal, partitions },
|
||||||
}) => {
|
}) => {
|
||||||
const outOfSyncReplicas = React.useMemo(() => {
|
const outOfSyncReplicas = React.useMemo(() => {
|
||||||
if (partitions === undefined || partitions.length === 0) {
|
if (partitions === undefined || partitions.length === 0) {
|
||||||
|
@ -14,27 +16,34 @@ const ListItem: React.FC<TopicWithDetailedInfo> = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
return partitions.reduce((memo: number, { replicas }) => {
|
return partitions.reduce((memo: number, { replicas }) => {
|
||||||
const outOfSync = replicas.filter(({ inSync }) => !inSync)
|
const outOfSync = replicas.filter(({ inSync }) => !inSync);
|
||||||
return memo + outOfSync.length;
|
return memo + outOfSync.length;
|
||||||
}, 0);
|
}, 0);
|
||||||
}, [partitions])
|
}, [partitions]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<NavLink exact to={`topics/${name}`} activeClassName="is-active" className="title is-6">
|
<NavLink
|
||||||
|
exact
|
||||||
|
to={`topics/${name}`}
|
||||||
|
activeClassName="is-active"
|
||||||
|
className="title is-6"
|
||||||
|
>
|
||||||
{name}
|
{name}
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</td>
|
</td>
|
||||||
<td>{partitions.length}</td>
|
<td>{partitions.length}</td>
|
||||||
<td>{outOfSyncReplicas}</td>
|
<td>{outOfSyncReplicas}</td>
|
||||||
<td>
|
<td>
|
||||||
<div className={cx('tag is-small', internal ? 'is-light' : 'is-success')}>
|
<div
|
||||||
|
className={cx('tag is-small', internal ? 'is-light' : 'is-success')}
|
||||||
|
>
|
||||||
{internal ? 'Internal' : 'External'}
|
{internal ? 'Internal' : 'External'}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default ListItem;
|
export default ListItem;
|
||||||
|
|
|
@ -1,35 +1,48 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { RootState, ClusterName, TopicFormData, TopicName, Action } from 'redux/interfaces';
|
import {
|
||||||
import New from './New';
|
RootState,
|
||||||
|
ClusterName,
|
||||||
|
TopicFormData,
|
||||||
|
TopicName,
|
||||||
|
Action,
|
||||||
|
} from 'redux/interfaces';
|
||||||
import { withRouter, RouteComponentProps } from 'react-router-dom';
|
import { withRouter, RouteComponentProps } from 'react-router-dom';
|
||||||
import { createTopic } from 'redux/actions';
|
import { createTopic } from 'redux/actions';
|
||||||
import { getTopicCreated } from 'redux/reducers/topics/selectors';
|
import { getTopicCreated } from 'redux/reducers/topics/selectors';
|
||||||
import { clusterTopicPath } from 'lib/paths';
|
import { clusterTopicPath } from 'lib/paths';
|
||||||
import { ThunkDispatch } from 'redux-thunk';
|
import { ThunkDispatch } from 'redux-thunk';
|
||||||
import * as actions from "../../../redux/actions/actions";
|
import New from './New';
|
||||||
|
import * as actions from '../../../redux/actions/actions';
|
||||||
|
|
||||||
interface RouteProps {
|
interface RouteProps {
|
||||||
clusterName: ClusterName;
|
clusterName: ClusterName;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface OwnProps extends RouteComponentProps<RouteProps> { }
|
type OwnProps = RouteComponentProps<RouteProps>;
|
||||||
|
|
||||||
const mapStateToProps = (state: RootState, { match: { params: { clusterName } } }: OwnProps) => ({
|
const mapStateToProps = (
|
||||||
|
state: RootState,
|
||||||
|
{
|
||||||
|
match: {
|
||||||
|
params: { clusterName },
|
||||||
|
},
|
||||||
|
}: OwnProps
|
||||||
|
) => ({
|
||||||
clusterName,
|
clusterName,
|
||||||
isTopicCreated: getTopicCreated(state),
|
isTopicCreated: getTopicCreated(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch: ThunkDispatch<RootState, undefined, Action>, { history }: OwnProps) => ({
|
const mapDispatchToProps = (
|
||||||
|
dispatch: ThunkDispatch<RootState, undefined, Action>,
|
||||||
|
{ history }: OwnProps
|
||||||
|
) => ({
|
||||||
createTopic: (clusterName: ClusterName, form: TopicFormData) => {
|
createTopic: (clusterName: ClusterName, form: TopicFormData) => {
|
||||||
dispatch(createTopic(clusterName, form));
|
dispatch(createTopic(clusterName, form));
|
||||||
},
|
},
|
||||||
redirectToTopicPath: (clusterName: ClusterName, topicName: TopicName) => {
|
redirectToTopicPath: (clusterName: ClusterName, topicName: TopicName) => {
|
||||||
history.push(clusterTopicPath(clusterName, topicName));
|
history.push(clusterTopicPath(clusterName, topicName));
|
||||||
},
|
},
|
||||||
resetUploadedState: (() => dispatch(actions.createTopicAction.failure()))
|
resetUploadedState: () => dispatch(actions.createTopicAction.failure()),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(New));
|
||||||
export default withRouter(
|
|
||||||
connect(mapStateToProps, mapDispatchToProps)(New)
|
|
||||||
);
|
|
||||||
|
|
|
@ -1,17 +1,24 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { fetchTopicList } from 'redux/actions';
|
import { fetchTopicList } from 'redux/actions';
|
||||||
import Topics from './Topics';
|
|
||||||
import { getIsTopicListFetched } from 'redux/reducers/topics/selectors';
|
import { getIsTopicListFetched } from 'redux/reducers/topics/selectors';
|
||||||
import { RootState, ClusterName } from 'redux/interfaces';
|
import { RootState, ClusterName } from 'redux/interfaces';
|
||||||
import { RouteComponentProps } from 'react-router-dom';
|
import { RouteComponentProps } from 'react-router-dom';
|
||||||
|
import Topics from './Topics';
|
||||||
|
|
||||||
interface RouteProps {
|
interface RouteProps {
|
||||||
clusterName: ClusterName;
|
clusterName: ClusterName;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface OwnProps extends RouteComponentProps<RouteProps> { }
|
type OwnProps = RouteComponentProps<RouteProps>;
|
||||||
|
|
||||||
const mapStateToProps = (state: RootState, { match: { params: { clusterName } }}: OwnProps) => ({
|
const mapStateToProps = (
|
||||||
|
state: RootState,
|
||||||
|
{
|
||||||
|
match: {
|
||||||
|
params: { clusterName },
|
||||||
|
},
|
||||||
|
}: OwnProps
|
||||||
|
) => ({
|
||||||
isFetched: getIsTopicListFetched(state),
|
isFetched: getIsTopicListFetched(state),
|
||||||
clusterName,
|
clusterName,
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { NavLink } from 'react-router-dom';
|
import { NavLink } from 'react-router-dom';
|
||||||
|
import { v4 } from 'uuid';
|
||||||
|
|
||||||
interface Link {
|
interface Link {
|
||||||
label: string;
|
label: string;
|
||||||
|
@ -10,18 +11,16 @@ interface Props {
|
||||||
links?: Link[];
|
links?: Link[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const Breadcrumb: React.FC<Props> = ({
|
const Breadcrumb: React.FC<Props> = ({ links, children }) => {
|
||||||
links,
|
|
||||||
children,
|
|
||||||
}) => {
|
|
||||||
return (
|
return (
|
||||||
<nav className="breadcrumb" aria-label="breadcrumbs">
|
<nav className="breadcrumb" aria-label="breadcrumbs">
|
||||||
<ul>
|
<ul>
|
||||||
{links && links.map(({ label, href }, index) => (
|
{links &&
|
||||||
<li key={`breadcrumb-item-key-${index}`}>
|
links.map(({ label, href }) => (
|
||||||
<NavLink to={href}>{label}</NavLink>
|
<li key={v4()}>
|
||||||
</li>
|
<NavLink to={href}>{label}</NavLink>
|
||||||
))}
|
</li>
|
||||||
|
))}
|
||||||
|
|
||||||
<li className="is-active">
|
<li className="is-active">
|
||||||
<span className="">{children}</span>
|
<span className="">{children}</span>
|
||||||
|
@ -29,6 +28,6 @@ const Breadcrumb: React.FC<Props> = ({
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default Breadcrumb;
|
export default Breadcrumb;
|
||||||
|
|
|
@ -5,19 +5,15 @@ interface Props {
|
||||||
title?: string;
|
title?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Indicator: React.FC<Props> = ({
|
const Indicator: React.FC<Props> = ({ label, title, children }) => {
|
||||||
label,
|
|
||||||
title,
|
|
||||||
children,
|
|
||||||
}) => {
|
|
||||||
return (
|
return (
|
||||||
<div className="level-item level-left">
|
<div className="level-item level-left">
|
||||||
<div title={title ? title : label}>
|
<div title={title || label}>
|
||||||
<p className="heading">{label}</p>
|
<p className="heading">{label}</p>
|
||||||
<p className="title">{children}</p>
|
<p className="title">{children}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default Indicator;
|
export default Indicator;
|
||||||
|
|
|
@ -13,16 +13,10 @@ const MetricsWrapper: React.FC<Props> = ({
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div className={cx('box', wrapperClassName)}>
|
<div className={cx('box', wrapperClassName)}>
|
||||||
{title && (
|
{title && <h5 className="subtitle is-6">{title}</h5>}
|
||||||
<h5 className="subtitle is-6">
|
<div className="level">{children}</div>
|
||||||
{title}
|
|
||||||
</h5>
|
|
||||||
)}
|
|
||||||
<div className="level">
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default MetricsWrapper;
|
export default MetricsWrapper;
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import { BrowserRouter } from 'react-router-dom';
|
import { BrowserRouter } from 'react-router-dom';
|
||||||
|
@ -16,7 +15,7 @@ ReactDOM.render(
|
||||||
<AppContainer />
|
<AppContainer />
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
</Provider>,
|
</Provider>,
|
||||||
document.getElementById('root'),
|
document.getElementById('root')
|
||||||
);
|
);
|
||||||
|
|
||||||
// If you want your app to work offline and load faster, you can change
|
// If you want your app to work offline and load faster, you can change
|
||||||
|
|
|
@ -5,26 +5,21 @@ type Callback = () => any;
|
||||||
const useInterval = (callback: Callback, delay: number) => {
|
const useInterval = (callback: Callback, delay: number) => {
|
||||||
const savedCallback = React.useRef<Callback>();
|
const savedCallback = React.useRef<Callback>();
|
||||||
|
|
||||||
React.useEffect(
|
React.useEffect(() => {
|
||||||
() => {
|
savedCallback.current = callback;
|
||||||
savedCallback.current = callback;
|
}, [callback]);
|
||||||
},
|
|
||||||
[callback],
|
|
||||||
);
|
|
||||||
|
|
||||||
React.useEffect(
|
// eslint-disable-next-line consistent-return
|
||||||
() => {
|
React.useEffect(() => {
|
||||||
const tick = () => {
|
const tick = () => {
|
||||||
savedCallback.current && savedCallback.current()
|
if (savedCallback.current) savedCallback.current();
|
||||||
};
|
};
|
||||||
|
|
||||||
if (delay !== null) {
|
if (delay !== null) {
|
||||||
const id = setInterval(tick, delay);
|
const id = setInterval(tick, delay);
|
||||||
return () => clearInterval(id);
|
return () => clearInterval(id);
|
||||||
}
|
}
|
||||||
},
|
}, [delay]);
|
||||||
[delay],
|
};
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default useInterval;
|
export default useInterval;
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
function formatBytes(bytes: number, decimals: number = 0) {
|
function formatBytes(bytes: number, decimals = 0) {
|
||||||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
||||||
if (bytes === 0) return [0, sizes[0]];
|
if (bytes === 0) return [0, sizes[0]];
|
||||||
|
|
||||||
const k = 1024;
|
const k = 1024;
|
||||||
const dm = decimals < 0 ? 0 : decimals;
|
const dm = decimals < 0 ? 0 : decimals;
|
||||||
|
|
||||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||||
|
|
||||||
return [parseFloat((bytes / Math.pow(k, i)).toFixed(dm)), sizes[i]];
|
return [parseFloat((bytes / k ** i).toFixed(dm)), sizes[i]];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default formatBytes;
|
export default formatBytes;
|
||||||
|
|
2
kafka-ui-react-app/src/react-app-env.d.ts
vendored
2
kafka-ui-react-app/src/react-app-env.d.ts
vendored
|
@ -1 +1 @@
|
||||||
/// <reference types="react-scripts" />
|
// / <reference types="react-scripts" />
|
||||||
|
|
|
@ -1,11 +1,5 @@
|
||||||
import {
|
import { Cluster } from 'redux/interfaces';
|
||||||
Cluster,
|
import { BASE_URL, BASE_PARAMS } from 'lib/constants';
|
||||||
} from 'redux/interfaces';
|
|
||||||
import {
|
|
||||||
BASE_URL,
|
|
||||||
BASE_PARAMS,
|
|
||||||
} from 'lib/constants';
|
|
||||||
|
|
||||||
export const getClusters = (): Promise<Cluster[]> =>
|
export const getClusters = (): Promise<Cluster[]> =>
|
||||||
fetch(`${BASE_URL}/clusters`, { ...BASE_PARAMS })
|
fetch(`${BASE_URL}/clusters`, { ...BASE_PARAMS }).then((res) => res.json());
|
||||||
.then(res => res.json());
|
|
||||||
|
|
|
@ -1,12 +1,23 @@
|
||||||
import { ClusterName } from '../interfaces/cluster';
|
import { ClusterName } from '../interfaces/cluster';
|
||||||
import { ConsumerGroup, ConsumerGroupID, ConsumerGroupDetails } from '../interfaces/consumerGroup';
|
import {
|
||||||
|
ConsumerGroup,
|
||||||
|
ConsumerGroupID,
|
||||||
|
ConsumerGroupDetails,
|
||||||
|
} from '../interfaces/consumerGroup';
|
||||||
import { BASE_PARAMS, BASE_URL } from '../../lib/constants';
|
import { BASE_PARAMS, BASE_URL } from '../../lib/constants';
|
||||||
|
|
||||||
|
export const getConsumerGroups = (
|
||||||
|
clusterName: ClusterName
|
||||||
|
): Promise<ConsumerGroup[]> =>
|
||||||
|
fetch(`${BASE_URL}/clusters/${clusterName}/consumerGroups`, {
|
||||||
|
...BASE_PARAMS,
|
||||||
|
}).then((res) => res.json());
|
||||||
|
|
||||||
export const getConsumerGroups = (clusterName: ClusterName): Promise<ConsumerGroup[]> =>
|
export const getConsumerGroupDetails = (
|
||||||
fetch(`${BASE_URL}/clusters/${clusterName}/consumerGroups`, { ...BASE_PARAMS })
|
clusterName: ClusterName,
|
||||||
.then(res => res.json());
|
consumerGroupID: ConsumerGroupID
|
||||||
|
): Promise<ConsumerGroupDetails> =>
|
||||||
export const getConsumerGroupDetails = (clusterName: ClusterName, consumerGroupID: ConsumerGroupID): Promise<ConsumerGroupDetails> =>
|
fetch(
|
||||||
fetch(`${BASE_URL}/clusters/${clusterName}/consumer-groups/${consumerGroupID}`, { ...BASE_PARAMS })
|
`${BASE_URL}/clusters/${clusterName}/consumer-groups/${consumerGroupID}`,
|
||||||
.then(res => res.json());
|
{ ...BASE_PARAMS }
|
||||||
|
).then((res) => res.json());
|
||||||
|
|
|
@ -6,9 +6,12 @@ export interface Broker {
|
||||||
segmentSize: number;
|
segmentSize: number;
|
||||||
partitionReplicas: number;
|
partitionReplicas: number;
|
||||||
bytesOutPerSec: number;
|
bytesOutPerSec: number;
|
||||||
};
|
}
|
||||||
|
|
||||||
export enum ZooKeeperStatus { offline, online };
|
export enum ZooKeeperStatus {
|
||||||
|
offline,
|
||||||
|
online,
|
||||||
|
}
|
||||||
|
|
||||||
export interface BrokerDiskUsage {
|
export interface BrokerDiskUsage {
|
||||||
brokerId: BrokerId;
|
brokerId: BrokerId;
|
||||||
|
@ -21,8 +24,8 @@ export interface BrokerMetrics {
|
||||||
activeControllers: number;
|
activeControllers: number;
|
||||||
onlinePartitionCount: number;
|
onlinePartitionCount: number;
|
||||||
offlinePartitionCount: number;
|
offlinePartitionCount: number;
|
||||||
inSyncReplicasCount: number,
|
inSyncReplicasCount: number;
|
||||||
outOfSyncReplicasCount: number,
|
outOfSyncReplicasCount: number;
|
||||||
underReplicatedPartitionCount: number;
|
underReplicatedPartitionCount: number;
|
||||||
diskUsage: BrokerDiskUsage[];
|
diskUsage: BrokerDiskUsage[];
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,4 +33,9 @@ export interface RootState {
|
||||||
|
|
||||||
export type Action = ActionType<typeof actions>;
|
export type Action = ActionType<typeof actions>;
|
||||||
|
|
||||||
export type PromiseThunk<T> = ThunkAction<Promise<T>, RootState, undefined, AnyAction>;
|
export type PromiseThunk<T> = ThunkAction<
|
||||||
|
Promise<T>,
|
||||||
|
RootState,
|
||||||
|
undefined,
|
||||||
|
AnyAction
|
||||||
|
>;
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import { Cluster, RootState, FetchStatus, ClusterStatus } from 'redux/interfaces';
|
import {
|
||||||
|
Cluster,
|
||||||
|
RootState,
|
||||||
|
FetchStatus,
|
||||||
|
ClusterStatus,
|
||||||
|
} from 'redux/interfaces';
|
||||||
import { createFetchingSelector } from 'redux/reducers/loader/selectors';
|
import { createFetchingSelector } from 'redux/reducers/loader/selectors';
|
||||||
|
|
||||||
const clustersState = ({ clusters }: RootState): Cluster[] => clusters;
|
const clustersState = ({ clusters }: RootState): Cluster[] => clusters;
|
||||||
|
@ -8,21 +13,18 @@ const getClusterListFetchingStatus = createFetchingSelector('GET_CLUSTERS');
|
||||||
|
|
||||||
export const getIsClusterListFetched = createSelector(
|
export const getIsClusterListFetched = createSelector(
|
||||||
getClusterListFetchingStatus,
|
getClusterListFetchingStatus,
|
||||||
(status) => status === FetchStatus.fetched,
|
(status) => status === FetchStatus.fetched
|
||||||
);
|
);
|
||||||
|
|
||||||
export const getClusterList = createSelector(clustersState, (clusters) => clusters);
|
export const getClusterList = createSelector(
|
||||||
|
clustersState,
|
||||||
export const getOnlineClusters = createSelector(
|
(clusters) => clusters
|
||||||
getClusterList,
|
|
||||||
(clusters) => clusters.filter(
|
|
||||||
({ status }) => status === ClusterStatus.Online,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
export const getOfflineClusters = createSelector(
|
export const getOnlineClusters = createSelector(getClusterList, (clusters) =>
|
||||||
getClusterList,
|
clusters.filter(({ status }) => status === ClusterStatus.Online)
|
||||||
(clusters) => clusters.filter(
|
);
|
||||||
({ status }) => status === ClusterStatus.Offline,
|
|
||||||
),
|
export const getOfflineClusters = createSelector(getClusterList, (clusters) =>
|
||||||
|
clusters.filter(({ status }) => status === ClusterStatus.Offline)
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,25 +1,35 @@
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import { RootState, FetchStatus } from 'redux/interfaces';
|
import { RootState, FetchStatus } from 'redux/interfaces';
|
||||||
import { createFetchingSelector } from 'redux/reducers/loader/selectors';
|
import { createFetchingSelector } from 'redux/reducers/loader/selectors';
|
||||||
import { ConsumerGroupID, ConsumerGroupsState } from '../../interfaces/consumerGroup';
|
import {
|
||||||
|
ConsumerGroupID,
|
||||||
|
ConsumerGroupsState,
|
||||||
|
} from '../../interfaces/consumerGroup';
|
||||||
|
|
||||||
|
const consumerGroupsState = ({
|
||||||
|
consumerGroups,
|
||||||
|
}: RootState): ConsumerGroupsState => consumerGroups;
|
||||||
|
|
||||||
const consumerGroupsState = ({ consumerGroups }: RootState): ConsumerGroupsState => consumerGroups;
|
const getConsumerGroupsMap = (state: RootState) =>
|
||||||
|
consumerGroupsState(state).byID;
|
||||||
|
const getConsumerGroupsIDsList = (state: RootState) =>
|
||||||
|
consumerGroupsState(state).allIDs;
|
||||||
|
|
||||||
const getConsumerGroupsMap = (state: RootState) => consumerGroupsState(state).byID;
|
const getConsumerGroupsListFetchingStatus = createFetchingSelector(
|
||||||
const getConsumerGroupsIDsList = (state: RootState) => consumerGroupsState(state).allIDs;
|
'GET_CONSUMER_GROUPS'
|
||||||
|
);
|
||||||
const getConsumerGroupsListFetchingStatus = createFetchingSelector('GET_CONSUMER_GROUPS');
|
const getConsumerGroupDetailsFetchingStatus = createFetchingSelector(
|
||||||
const getConsumerGroupDetailsFetchingStatus = createFetchingSelector('GET_CONSUMER_GROUP_DETAILS');
|
'GET_CONSUMER_GROUP_DETAILS'
|
||||||
|
);
|
||||||
|
|
||||||
export const getIsConsumerGroupsListFetched = createSelector(
|
export const getIsConsumerGroupsListFetched = createSelector(
|
||||||
getConsumerGroupsListFetchingStatus,
|
getConsumerGroupsListFetchingStatus,
|
||||||
(status) => status === FetchStatus.fetched,
|
(status) => status === FetchStatus.fetched
|
||||||
);
|
);
|
||||||
|
|
||||||
export const getIsConsumerGroupDetailsFetched = createSelector(
|
export const getIsConsumerGroupDetailsFetched = createSelector(
|
||||||
getConsumerGroupDetailsFetchingStatus,
|
getConsumerGroupDetailsFetchingStatus,
|
||||||
(status) => status === FetchStatus.fetched,
|
(status) => status === FetchStatus.fetched
|
||||||
);
|
);
|
||||||
|
|
||||||
export const getConsumerGroupsList = createSelector(
|
export const getConsumerGroupsList = createSelector(
|
||||||
|
@ -31,14 +41,15 @@ export const getConsumerGroupsList = createSelector(
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return ids.map(key => byID[key]);
|
return ids.map((key) => byID[key]);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const getConsumerGroupID = (_: RootState, consumerGroupID: ConsumerGroupID) => consumerGroupID;
|
const getConsumerGroupID = (_: RootState, consumerGroupID: ConsumerGroupID) =>
|
||||||
|
consumerGroupID;
|
||||||
|
|
||||||
export const getConsumerGroupByID = createSelector(
|
export const getConsumerGroupByID = createSelector(
|
||||||
getConsumerGroupsMap,
|
getConsumerGroupsMap,
|
||||||
getConsumerGroupID,
|
getConsumerGroupID,
|
||||||
(consumerGroups, consumerGroupID) => consumerGroups[consumerGroupID],
|
(consumerGroups, consumerGroupID) => consumerGroups[consumerGroupID]
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { combineReducers } from 'redux';
|
import { combineReducers } from 'redux';
|
||||||
|
import { RootState } from 'redux/interfaces';
|
||||||
import topics from './topics/reducer';
|
import topics from './topics/reducer';
|
||||||
import clusters from './clusters/reducer';
|
import clusters from './clusters/reducer';
|
||||||
import brokers from './brokers/reducer';
|
import brokers from './brokers/reducer';
|
||||||
import consumerGroups from './consumerGroups/reducer';
|
import consumerGroups from './consumerGroups/reducer';
|
||||||
import loader from './loader/reducer';
|
import loader from './loader/reducer';
|
||||||
import { RootState } from 'redux/interfaces';
|
|
||||||
|
|
||||||
export default combineReducers<RootState>({
|
export default combineReducers<RootState>({
|
||||||
topics,
|
topics,
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
import {
|
import { FetchStatus, Action, LoaderState } from 'redux/interfaces';
|
||||||
FetchStatus,
|
|
||||||
Action,
|
|
||||||
LoaderState,
|
|
||||||
} from 'redux/interfaces';
|
|
||||||
|
|
||||||
export const initialState: LoaderState = {};
|
export const initialState: LoaderState = {};
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { RootState, FetchStatus } from 'redux/interfaces';
|
import { RootState, FetchStatus } from 'redux/interfaces';
|
||||||
|
|
||||||
export const createFetchingSelector = (action: string) =>
|
export const createFetchingSelector = (action: string) => (state: RootState) =>
|
||||||
(state: RootState) => (state.loader[action] || FetchStatus.notFetched);
|
state.loader[action] || FetchStatus.notFetched;
|
||||||
|
|
|
@ -5,13 +5,12 @@ import rootReducer from '../../reducers';
|
||||||
export default () => {
|
export default () => {
|
||||||
const middlewares = [thunk];
|
const middlewares = [thunk];
|
||||||
|
|
||||||
const composeEnhancers = (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
|
const composeEnhancers =
|
||||||
|
(window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
|
||||||
|
|
||||||
const enhancer = composeEnhancers(
|
const enhancer = composeEnhancers(applyMiddleware(...middlewares));
|
||||||
applyMiddleware(...middlewares),
|
|
||||||
);
|
|
||||||
|
|
||||||
const store = createStore(rootReducer, undefined, enhancer);
|
const store = createStore(rootReducer, undefined, enhancer);
|
||||||
|
|
||||||
return store
|
return store;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
import devConfigureStore from './dev';
|
import devConfigureStore from './dev';
|
||||||
import prodConfigureStore from './prod';
|
import prodConfigureStore from './prod';
|
||||||
|
|
||||||
const configureStore = (process.env.NODE_ENV === 'production') ? prodConfigureStore : devConfigureStore
|
const configureStore =
|
||||||
|
process.env.NODE_ENV === 'production'
|
||||||
|
? prodConfigureStore
|
||||||
|
: devConfigureStore;
|
||||||
|
|
||||||
export default configureStore;
|
export default configureStore;
|
||||||
|
|
|
@ -3,11 +3,11 @@ import thunk from 'redux-thunk';
|
||||||
import rootReducer from '../../reducers';
|
import rootReducer from '../../reducers';
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
const middlewares = [thunk]
|
const middlewares = [thunk];
|
||||||
|
|
||||||
const enhancer = applyMiddleware(...middlewares);
|
const enhancer = applyMiddleware(...middlewares);
|
||||||
|
|
||||||
const store = createStore(rootReducer, undefined, enhancer);
|
const store = createStore(rootReducer, undefined, enhancer);
|
||||||
|
|
||||||
return store
|
return store;
|
||||||
};
|
};
|
||||||
|
|
|
@ -25,47 +25,10 @@ type Config = {
|
||||||
onUpdate?: (registration: ServiceWorkerRegistration) => void;
|
onUpdate?: (registration: ServiceWorkerRegistration) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function register(config?: Config) {
|
|
||||||
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
|
|
||||||
// The URL constructor is available in all browsers that support SW.
|
|
||||||
const publicUrl = new URL(
|
|
||||||
process.env.PUBLIC_URL,
|
|
||||||
window.location.href
|
|
||||||
);
|
|
||||||
if (publicUrl.origin !== window.location.origin) {
|
|
||||||
// Our service worker won't work if PUBLIC_URL is on a different origin
|
|
||||||
// from what our page is served on. This might happen if a CDN is used to
|
|
||||||
// serve assets; see https://github.com/facebook/create-react-app/issues/2374
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener('load', () => {
|
|
||||||
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
|
|
||||||
|
|
||||||
if (isLocalhost) {
|
|
||||||
// This is running on localhost. Let's check if a service worker still exists or not.
|
|
||||||
checkValidServiceWorker(swUrl, config);
|
|
||||||
|
|
||||||
// Add some additional logging to localhost, pointing developers to the
|
|
||||||
// service worker/PWA documentation.
|
|
||||||
navigator.serviceWorker.ready.then(() => {
|
|
||||||
console.log(
|
|
||||||
'This web app is being served cache-first by a service ' +
|
|
||||||
'worker. To learn more, visit https://bit.ly/CRA-PWA'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// Is not localhost. Just register service worker
|
|
||||||
registerValidSW(swUrl, config);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function registerValidSW(swUrl: string, config?: Config) {
|
function registerValidSW(swUrl: string, config?: Config) {
|
||||||
navigator.serviceWorker
|
navigator.serviceWorker
|
||||||
.register(swUrl)
|
.register(swUrl)
|
||||||
.then(registration => {
|
.then((registration) => {
|
||||||
registration.onupdatefound = () => {
|
registration.onupdatefound = () => {
|
||||||
const installingWorker = registration.installing;
|
const installingWorker = registration.installing;
|
||||||
if (installingWorker == null) {
|
if (installingWorker == null) {
|
||||||
|
@ -101,7 +64,7 @@ function registerValidSW(swUrl: string, config?: Config) {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch((error) => {
|
||||||
console.error('Error during service worker registration:', error);
|
console.error('Error during service worker registration:', error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -109,9 +72,9 @@ function registerValidSW(swUrl: string, config?: Config) {
|
||||||
function checkValidServiceWorker(swUrl: string, config?: Config) {
|
function checkValidServiceWorker(swUrl: string, config?: Config) {
|
||||||
// Check if the service worker can be found. If it can't reload the page.
|
// Check if the service worker can be found. If it can't reload the page.
|
||||||
fetch(swUrl, {
|
fetch(swUrl, {
|
||||||
headers: { 'Service-Worker': 'script' }
|
headers: { 'Service-Worker': 'script' },
|
||||||
})
|
})
|
||||||
.then(response => {
|
.then((response) => {
|
||||||
// Ensure service worker exists, and that we really are getting a JS file.
|
// Ensure service worker exists, and that we really are getting a JS file.
|
||||||
const contentType = response.headers.get('content-type');
|
const contentType = response.headers.get('content-type');
|
||||||
if (
|
if (
|
||||||
|
@ -119,7 +82,7 @@ function checkValidServiceWorker(swUrl: string, config?: Config) {
|
||||||
(contentType != null && contentType.indexOf('javascript') === -1)
|
(contentType != null && contentType.indexOf('javascript') === -1)
|
||||||
) {
|
) {
|
||||||
// No service worker found. Probably a different app. Reload the page.
|
// No service worker found. Probably a different app. Reload the page.
|
||||||
navigator.serviceWorker.ready.then(registration => {
|
navigator.serviceWorker.ready.then((registration) => {
|
||||||
registration.unregister().then(() => {
|
registration.unregister().then(() => {
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
});
|
});
|
||||||
|
@ -136,9 +99,44 @@ function checkValidServiceWorker(swUrl: string, config?: Config) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function register(config?: Config) {
|
||||||
|
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
|
||||||
|
const url = process.env.PUBLIC_URL || 'localhost';
|
||||||
|
// The URL constructor is available in all browsers that support SW.
|
||||||
|
const publicUrl = new URL(url, window.location.href);
|
||||||
|
if (publicUrl.origin !== window.location.origin) {
|
||||||
|
// Our service worker won't work if PUBLIC_URL is on a different origin
|
||||||
|
// from what our page is served on. This might happen if a CDN is used to
|
||||||
|
// serve assets; see https://github.com/facebook/create-react-app/issues/2374
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('load', () => {
|
||||||
|
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
|
||||||
|
|
||||||
|
if (isLocalhost) {
|
||||||
|
// This is running on localhost. Let's check if a service worker still exists or not.
|
||||||
|
checkValidServiceWorker(swUrl, config);
|
||||||
|
|
||||||
|
// Add some additional logging to localhost, pointing developers to the
|
||||||
|
// service worker/PWA documentation.
|
||||||
|
navigator.serviceWorker.ready.then(() => {
|
||||||
|
console.log(
|
||||||
|
'This web app is being served cache-first by a service ' +
|
||||||
|
'worker. To learn more, visit https://bit.ly/CRA-PWA'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Is not localhost. Just register service worker
|
||||||
|
registerValidSW(swUrl, config);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function unregister() {
|
export function unregister() {
|
||||||
if ('serviceWorker' in navigator) {
|
if ('serviceWorker' in navigator) {
|
||||||
navigator.serviceWorker.ready.then(registration => {
|
navigator.serviceWorker.ready.then((registration) => {
|
||||||
registration.unregister();
|
registration.unregister();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,4 +2,5 @@
|
||||||
// allows you to do things like:
|
// allows you to do things like:
|
||||||
// expect(element).toHaveTextContent(/react/i)
|
// expect(element).toHaveTextContent(/react/i)
|
||||||
// learn more: https://github.com/testing-library/jest-dom
|
// learn more: https://github.com/testing-library/jest-dom
|
||||||
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||||
import '@testing-library/jest-dom/extend-expect';
|
import '@testing-library/jest-dom/extend-expect';
|
||||||
|
|
Loading…
Add table
Reference in a new issue