[UI] Use new loader reducer + switch

This commit is contained in:
Oleg Shuralev 2020-01-06 14:46:25 +03:00
parent a70a688b64
commit ee1fff204c
No known key found for this signature in database
GPG key ID: 0459DF80E1A2FD1B
24 changed files with 162 additions and 122 deletions

View file

@ -2930,6 +2930,11 @@
"resolved": "https://registry.npmjs.org/bulma/-/bulma-0.8.0.tgz",
"integrity": "sha512-nhf3rGyiZh/VM7FrSJ/5KeLlfaFkXz0nYcXriynfPH4vVpnxnqyEwaNGdNCVzHyyCA3cHgkQAMpdF/SFbFGZfA=="
},
"bulma-switch": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/bulma-switch/-/bulma-switch-2.0.0.tgz",
"integrity": "sha512-myD38zeUfjmdduq+pXabhJEe3x2hQP48l/OI+Y0fO3HdDynZUY/VJygucvEAJKRjr4HxD5DnEm4yx+oDOBXpAA=="
},
"bytes": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",

View file

@ -16,6 +16,7 @@
"@types/redux": "^3.6.0",
"@types/redux-thunk": "^2.1.0",
"bulma": "^0.8.0",
"bulma-switch": "^2.0.0",
"classnames": "^2.2.6",
"node-sass": "^4.13.0",
"react": "^16.12.0",

View file

@ -4,14 +4,37 @@ import ListItem from './ListItem';
interface Props {
topics: Topic[];
externalTopics: Topic[];
}
const List: React.FC<Props> = ({
topics,
externalTopics,
}) => {
const [showInternal, setShowInternal] = React.useState<boolean>(true);
const handleSwitch = () => setShowInternal(!showInternal);
const items = showInternal ? topics : externalTopics;
return (
<>
<div className="section">
<div className="box">
<div className="field">
<input
id="switchRoundedDefault"
type="checkbox"
name="switchRoundedDefault"
className="switch is-rounded"
checked={showInternal}
onChange={handleSwitch}
/>
<label htmlFor="switchRoundedDefault">
Show Internal Topics
</label>
</div>
</div>
<div className="box">
<table className="table is-striped is-fullwidth">
<thead>
@ -22,7 +45,7 @@ const List: React.FC<Props> = ({
</tr>
</thead>
<tbody>
{topics.map((topic) => (
{items.map((topic) => (
<ListItem
key={topic.name}
{...topic}

View file

@ -1,14 +1,11 @@
import { connect } from 'react-redux';
import {
getTopicList,
getTotalBrokers,
} from 'redux/reducers/topics/selectors';
import { RootState } from 'types';
import { getTopicList, getExternalTopicList } from 'redux/reducers/topics/selectors';
import List from './List';
const mapStateToProps = (state: RootState) => ({
topics: getTopicList(state),
totalBrokers: getTotalBrokers(state),
externalTopics: getExternalTopicList(state),
});

View file

@ -1,8 +1,6 @@
import { connect } from 'react-redux';
import {
fetchTopicList,
fetchBrokers,
} from 'redux/reducers/topics/thunks';
import { fetchTopicList } from 'redux/reducers/topics/thunks';
import { fetchBrokers } from 'redux/reducers/brokers/thunks';
import Topics from './Topics';
import { getIsTopicListFetched } from 'redux/reducers/topics/selectors';
import { RootState, ClusterId } from 'types';

View file

@ -1,7 +1,9 @@
import topicsActionType from './topics/actionType';
import clustersActionType from './clusters/actionType';
import brokersActionType from './brokers/actionType';
export default {
...topicsActionType,
...clustersActionType,
...brokersActionType,
};

View file

@ -0,0 +1,7 @@
enum ActionType {
GET_BROKERS__REQUEST = 'GET_BROKERS__REQUEST',
GET_BROKERS__SUCCESS = 'GET_BROKERS__SUCCESS',
GET_BROKERS__FAILURE = 'GET_BROKERS__FAILURE',
}
export default ActionType;

View file

@ -0,0 +1,9 @@
import { createAsyncAction} from 'typesafe-actions';
import ActionType from './actionType';
import { Broker } from 'types';
export const fetchBrokersAction = createAsyncAction(
ActionType.GET_BROKERS__REQUEST,
ActionType.GET_BROKERS__SUCCESS,
ActionType.GET_BROKERS__FAILURE,
)<undefined, Broker[], undefined>();

View file

@ -0,0 +1,15 @@
import { Broker, Action } from 'types';
import actionType from 'redux/reducers/actionType';
export const initialState: Broker[] = [];
const reducer = (state = initialState, action: Action): Broker[] => {
switch (action.type) {
case actionType.GET_BROKERS__SUCCESS:
return action.payload;
default:
return state;
}
};
export default reducer;

View file

@ -0,0 +1,20 @@
import { createSelector } from 'reselect';
import { RootState, FetchStatus, Broker } from 'types';
import { createFetchingSelector } from 'redux/reducers/loader/selectors';
const brokersState = ({ brokers }: RootState): Broker[] => brokers;
const getBrokerListFetchingStatus = createFetchingSelector('GET_BROKERS');
export const getIsBrokerListFetched = createSelector(
getBrokerListFetchingStatus,
(status) => status === FetchStatus.fetched,
);
const getBrokerList = createSelector(brokersState, (brokers) => brokers);
export const getTotalBrokers = createSelector(
getIsBrokerListFetched,
getBrokerList,
(isFetched, brokers) => (isFetched && brokers !== undefined ? brokers.length : undefined),
);

View file

@ -0,0 +1,14 @@
import { getBrokers } from 'lib/api';
import { fetchBrokersAction } from './actions';
import { PromiseThunk, ClusterId } from 'types';
export const fetchBrokers = (clusterId: ClusterId): PromiseThunk<void> => async (dispatch) => {
dispatch(fetchBrokersAction.request());
try {
const { brokers } = await getBrokers(clusterId);
dispatch(fetchBrokersAction.success(brokers));
} catch (e) {
dispatch(fetchBrokersAction.failure());
}
}

View file

@ -1,9 +1,9 @@
import { ClustersState, Action } from 'types';
import { Cluster, Action } from 'types';
import actionType from 'redux/reducers/actionType';
export const initialState: ClustersState = [];
export const initialState: Cluster[] = [];
const reducer = (state = initialState, action: Action): ClustersState => {
const reducer = (state = initialState, action: Action): Cluster[] => {
switch (action.type) {
case actionType.GET_CLUSTERS__SUCCESS:
return action.payload;

View file

@ -1,11 +1,14 @@
import { createSelector } from 'reselect';
import { ClustersState, RootState, FetchStatus } from 'types';
import { Cluster, RootState, FetchStatus } from 'types';
import { createFetchingSelector } from 'redux/reducers/loader/selectors';
const clustersState = ({ clusters }: RootState): ClustersState => clusters;
const clustersState = ({ clusters }: RootState): Cluster[] => clusters;
const getClusterListFetchingStatus = createFetchingSelector('GET_CLUSTERS');
export const getIsClusterListFetched = createSelector(getClusterListFetchingStatus, (status) => status === FetchStatus.fetched);
export const getIsClusterListFetched = createSelector(
getClusterListFetchingStatus,
(status) => status === FetchStatus.fetched,
);
export const getClusterList = createSelector(clustersState, (items) => items);
export const getClusterList = createSelector(clustersState, (clusters) => clusters);

View file

@ -1,11 +1,13 @@
import { combineReducers } from 'redux';
import topics from './topics/reducer';
import clusters from './clusters/reducer';
import brokers from './brokers/reducer';
import loader from './loader/reducer';
import { RootState } from 'types';
export default combineReducers<RootState>({
topics,
clusters,
brokers,
loader,
});

View file

@ -1,11 +1,7 @@
enum ActionType {
TOPICS__FETCH_REQUEST = 'TOPICS__FETCH_REQUEST',
TOPICS__FETCH_SUCCESS = 'TOPICS__FETCH_SUCCESS',
TOPICS__FETCH_FAILURE = 'TOPICS__FETCH_FAILURE',
BROKERS__FETCH_REQUEST = 'BROKERS__FETCH_REQUEST',
BROKERS__FETCH_SUCCESS = 'BROKERS__FETCH_SUCCESS',
BROKERS__FETCH_FAILURE = 'BROKERS__FETCH_FAILURE',
GET_TOPICS__REQUEST = 'GET_TOPICS__REQUEST',
GET_TOPICS__SUCCESS = 'GET_TOPICS__SUCCESS',
GET_TOPICS__FAILURE = 'GET_TOPICS__FAILURE',
}
export default ActionType;

View file

@ -1,15 +1,9 @@
import { createAsyncAction} from 'typesafe-actions';
import ActionType from './actionType';
import { Topic, Broker } from 'types';
import { Topic} from 'types';
export const fetchTopicListAction = createAsyncAction(
ActionType.TOPICS__FETCH_REQUEST,
ActionType.TOPICS__FETCH_SUCCESS,
ActionType.TOPICS__FETCH_FAILURE,
ActionType.GET_TOPICS__REQUEST,
ActionType.GET_TOPICS__SUCCESS,
ActionType.GET_TOPICS__FAILURE,
)<undefined, Topic[], undefined>();
export const fetchBrokersAction = createAsyncAction(
ActionType.BROKERS__FETCH_REQUEST,
ActionType.BROKERS__FETCH_SUCCESS,
ActionType.BROKERS__FETCH_FAILURE,
)<undefined, Broker[], undefined>();

View file

@ -1,46 +1,12 @@
import { TopicsState, FetchStatus, Action } from 'types';
import { Topic, Action } from 'types';
import actionType from 'redux/reducers/actionType';
export const initialState: TopicsState = {
fetchStatus: FetchStatus.notFetched,
items: [],
brokers: undefined,
};
export const initialState: Topic[] = [];
const reducer = (state = initialState, action: Action): TopicsState => {
const reducer = (state = initialState, action: Action): Topic[] => {
switch (action.type) {
case actionType.TOPICS__FETCH_REQUEST:
return {
...state,
fetchStatus: FetchStatus.fetching,
};
case actionType.TOPICS__FETCH_SUCCESS:
return {
...state,
fetchStatus: FetchStatus.fetched,
items: action.payload,
};
case actionType.TOPICS__FETCH_FAILURE:
return {
...state,
fetchStatus: FetchStatus.errorFetching,
};
case actionType.BROKERS__FETCH_REQUEST:
return {
...state,
brokers: undefined,
};
case actionType.BROKERS__FETCH_SUCCESS:
return {
...state,
brokers: action.payload,
};
case actionType.BROKERS__FETCH_FAILURE:
return {
...state,
brokers: undefined,
};
case actionType.GET_TOPICS__SUCCESS:
return action.payload;
default:
return state;
}

View file

@ -1,15 +1,25 @@
import { createSelector } from 'reselect';
import { TopicsState, RootState, Topic, TopicName, FetchStatus } from 'types';
import { RootState, Topic, TopicName, FetchStatus } from 'types';
import { createFetchingSelector } from 'redux/reducers/loader/selectors';
const topicsState = ({ topics }: RootState): TopicsState => topics;
const topicsState = ({ topics }: RootState): Topic[] => topics;
export const getIsTopicListFetched = createSelector(topicsState, ({ fetchStatus }) => fetchStatus === FetchStatus.fetched);
const getTopicListFetchingStatus = createFetchingSelector('GET_TOPICS');
export const getTopicList = createSelector(topicsState, ({ items }) => items);
export const getIsTopicListFetched = createSelector(
getTopicListFetchingStatus,
(status) => status === FetchStatus.fetched,
);
export const getTotalBrokers = createSelector(
export const getTopicList = createSelector(
getIsTopicListFetched,
topicsState,
({ brokers }) => (brokers !== undefined ? brokers.length : undefined),
(isFetched, topics) => isFetched ? topics : [],
);
export const getExternalTopicList = createSelector(
getTopicList,
(topics) => topics.filter(({ internal }) => !internal),
);
interface TopicMap {[key: string]: Topic};

View file

@ -1,14 +1,6 @@
import {
getTopics,
getTopic,
getBrokers,
} from 'lib/api';
import {
fetchTopicListAction,
fetchBrokersAction,
} from './actions';
import { Topic, TopicName, PromiseThunk, ClusterId } from 'types';
import { getTopics } from 'lib/api';
import { fetchTopicListAction } from './actions';
import { PromiseThunk, ClusterId } from 'types';
export const fetchTopicList = (clusterId: ClusterId): PromiseThunk<void> => async (dispatch) => {
dispatch(fetchTopicListAction.request());
@ -21,13 +13,3 @@ export const fetchTopicList = (clusterId: ClusterId): PromiseThunk<void> => asyn
dispatch(fetchTopicListAction.failure());
}
}
export const fetchBrokers = (clusterId: ClusterId): PromiseThunk<void> => async (dispatch) => {
dispatch(fetchBrokersAction.request());
try {
const { brokers } = await getBrokers(clusterId);
dispatch(fetchBrokersAction.success(brokers));
} catch (e) {
dispatch(fetchBrokersAction.failure());
}
}

View file

@ -5,3 +5,4 @@
@import "../../node_modules/bulma/sass/components/_all.sass";
@import "../../node_modules/bulma/sass/grid/_all.sass";
@import "../../node_modules/bulma/sass/layout/_all.sass";
@import "../../node_modules/bulma-switch/src/sass/index.sass";

View file

@ -0,0 +1,4 @@
export interface Broker {
id: string,
host: "broker",
};

View file

@ -14,5 +14,3 @@ export interface Cluster {
onlinePartitionCount: number;
topicCount: number;
}
export type ClustersState = Cluster[];

View file

@ -1,14 +1,19 @@
import { AnyAction } from 'redux';
import { ActionType } from 'typesafe-actions';
import { ThunkAction } from 'redux-thunk';
import * as topicsActions from 'redux/reducers/topics/actions';
import * as clustersActions from 'redux/reducers/clusters/actions';
import { ThunkAction } from 'redux-thunk';
import { TopicsState } from './topic';
import { AnyAction } from 'redux';
import { ClustersState } from './cluster';
import * as brokersActions from 'redux/reducers/brokers/actions';
import { Topic } from './topic';
import { Cluster } from './cluster';
import { Broker } from './broker';
import { LoaderState } from './loader';
export * from './topic';
export * from './cluster';
export * from './broker';
export * from './loader';
export enum FetchStatus {
@ -19,11 +24,12 @@ export enum FetchStatus {
}
export interface RootState {
topics: TopicsState;
clusters: ClustersState;
topics: Topic[];
clusters: Cluster[];
brokers: Broker[];
loader: LoaderState;
}
export type Action = ActionType<typeof topicsActions | typeof clustersActions>;
export type Action = ActionType<typeof topicsActions | typeof clustersActions | typeof brokersActions>;
export type PromiseThunk<T> = ThunkAction<Promise<T>, RootState, undefined, AnyAction>;

View file

@ -1,5 +1,3 @@
import { FetchStatus } from 'types';
export type TopicName = string;
export interface TopicConfigs {
[key: string]: string;
@ -22,14 +20,3 @@ export interface Topic {
internal: boolean;
partitions: TopicPartition[];
}
export interface TopicsState {
fetchStatus: FetchStatus;
items: Topic[];
brokers?: Broker[];
}
export interface Broker {
id: 1,
host: "broker",
};