[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", "resolved": "https://registry.npmjs.org/bulma/-/bulma-0.8.0.tgz",
"integrity": "sha512-nhf3rGyiZh/VM7FrSJ/5KeLlfaFkXz0nYcXriynfPH4vVpnxnqyEwaNGdNCVzHyyCA3cHgkQAMpdF/SFbFGZfA==" "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": { "bytes": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",

View file

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

View file

@ -4,14 +4,37 @@ import ListItem from './ListItem';
interface Props { interface Props {
topics: Topic[]; topics: Topic[];
externalTopics: Topic[];
} }
const List: React.FC<Props> = ({ const List: React.FC<Props> = ({
topics, topics,
externalTopics,
}) => { }) => {
const [showInternal, setShowInternal] = React.useState<boolean>(true);
const handleSwitch = () => setShowInternal(!showInternal);
const items = showInternal ? topics : externalTopics;
return ( return (
<> <>
<div className="section"> <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"> <div className="box">
<table className="table is-striped is-fullwidth"> <table className="table is-striped is-fullwidth">
<thead> <thead>
@ -22,7 +45,7 @@ const List: React.FC<Props> = ({
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{topics.map((topic) => ( {items.map((topic) => (
<ListItem <ListItem
key={topic.name} key={topic.name}
{...topic} {...topic}

View file

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

View file

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

View file

@ -1,7 +1,9 @@
import topicsActionType from './topics/actionType'; import topicsActionType from './topics/actionType';
import clustersActionType from './clusters/actionType'; import clustersActionType from './clusters/actionType';
import brokersActionType from './brokers/actionType';
export default { export default {
...topicsActionType, ...topicsActionType,
...clustersActionType, ...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'; 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) { switch (action.type) {
case actionType.GET_CLUSTERS__SUCCESS: case actionType.GET_CLUSTERS__SUCCESS:
return action.payload; return action.payload;

View file

@ -1,11 +1,14 @@
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import { ClustersState, RootState, FetchStatus } from 'types'; import { Cluster, RootState, FetchStatus } from 'types';
import { createFetchingSelector } from 'redux/reducers/loader/selectors'; import { createFetchingSelector } from 'redux/reducers/loader/selectors';
const clustersState = ({ clusters }: RootState): ClustersState => clusters; const clustersState = ({ clusters }: RootState): Cluster[] => clusters;
const getClusterListFetchingStatus = createFetchingSelector('GET_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 { combineReducers } from 'redux';
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 loader from './loader/reducer'; import loader from './loader/reducer';
import { RootState } from 'types'; import { RootState } from 'types';
export default combineReducers<RootState>({ export default combineReducers<RootState>({
topics, topics,
clusters, clusters,
brokers,
loader, loader,
}); });

View file

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

View file

@ -1,15 +1,9 @@
import { createAsyncAction} from 'typesafe-actions'; import { createAsyncAction} from 'typesafe-actions';
import ActionType from './actionType'; import ActionType from './actionType';
import { Topic, Broker } from 'types'; import { Topic} from 'types';
export const fetchTopicListAction = createAsyncAction( export const fetchTopicListAction = createAsyncAction(
ActionType.TOPICS__FETCH_REQUEST, ActionType.GET_TOPICS__REQUEST,
ActionType.TOPICS__FETCH_SUCCESS, ActionType.GET_TOPICS__SUCCESS,
ActionType.TOPICS__FETCH_FAILURE, ActionType.GET_TOPICS__FAILURE,
)<undefined, Topic[], undefined>(); )<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'; import actionType from 'redux/reducers/actionType';
export const initialState: TopicsState = { export const initialState: Topic[] = [];
fetchStatus: FetchStatus.notFetched,
items: [],
brokers: undefined,
};
const reducer = (state = initialState, action: Action): TopicsState => { const reducer = (state = initialState, action: Action): Topic[] => {
switch (action.type) { switch (action.type) {
case actionType.TOPICS__FETCH_REQUEST: case actionType.GET_TOPICS__SUCCESS:
return { return action.payload;
...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,
};
default: default:
return state; return state;
} }

View file

@ -1,15 +1,25 @@
import { createSelector } from 'reselect'; 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, 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}; interface TopicMap {[key: string]: Topic};

View file

@ -1,14 +1,6 @@
import { import { getTopics } from 'lib/api';
getTopics, import { fetchTopicListAction } from './actions';
getTopic, import { PromiseThunk, ClusterId } from 'types';
getBrokers,
} from 'lib/api';
import {
fetchTopicListAction,
fetchBrokersAction,
} from './actions';
import { Topic, TopicName, PromiseThunk, ClusterId } from 'types';
export const fetchTopicList = (clusterId: ClusterId): PromiseThunk<void> => async (dispatch) => { export const fetchTopicList = (clusterId: ClusterId): PromiseThunk<void> => async (dispatch) => {
dispatch(fetchTopicListAction.request()); dispatch(fetchTopicListAction.request());
@ -21,13 +13,3 @@ export const fetchTopicList = (clusterId: ClusterId): PromiseThunk<void> => asyn
dispatch(fetchTopicListAction.failure()); 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/components/_all.sass";
@import "../../node_modules/bulma/sass/grid/_all.sass"; @import "../../node_modules/bulma/sass/grid/_all.sass";
@import "../../node_modules/bulma/sass/layout/_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; onlinePartitionCount: number;
topicCount: number; topicCount: number;
} }
export type ClustersState = Cluster[];

View file

@ -1,14 +1,19 @@
import { AnyAction } from 'redux';
import { ActionType } from 'typesafe-actions'; import { ActionType } from 'typesafe-actions';
import { ThunkAction } from 'redux-thunk';
import * as topicsActions from 'redux/reducers/topics/actions'; import * as topicsActions from 'redux/reducers/topics/actions';
import * as clustersActions from 'redux/reducers/clusters/actions'; import * as clustersActions from 'redux/reducers/clusters/actions';
import { ThunkAction } from 'redux-thunk'; import * as brokersActions from 'redux/reducers/brokers/actions';
import { TopicsState } from './topic';
import { AnyAction } from 'redux'; import { Topic } from './topic';
import { ClustersState } from './cluster'; import { Cluster } from './cluster';
import { Broker } from './broker';
import { LoaderState } from './loader'; import { LoaderState } from './loader';
export * from './topic'; export * from './topic';
export * from './cluster'; export * from './cluster';
export * from './broker';
export * from './loader'; export * from './loader';
export enum FetchStatus { export enum FetchStatus {
@ -19,11 +24,12 @@ export enum FetchStatus {
} }
export interface RootState { export interface RootState {
topics: TopicsState; topics: Topic[];
clusters: ClustersState; clusters: Cluster[];
brokers: Broker[];
loader: LoaderState; 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>; 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 type TopicName = string;
export interface TopicConfigs { export interface TopicConfigs {
[key: string]: string; [key: string]: string;
@ -22,14 +20,3 @@ export interface Topic {
internal: boolean; internal: boolean;
partitions: TopicPartition[]; partitions: TopicPartition[];
} }
export interface TopicsState {
fetchStatus: FetchStatus;
items: Topic[];
brokers?: Broker[];
}
export interface Broker {
id: 1,
host: "broker",
};