diff --git a/src/Routes/Base/SidebarLeft/index.js b/src/Routes/Base/SidebarLeft/index.js index 5f82a2275..ec5f522da 100644 --- a/src/Routes/Base/SidebarLeft/index.js +++ b/src/Routes/Base/SidebarLeft/index.js @@ -72,8 +72,6 @@ const LogoContainer = styled.div` display: flex; `; -const equalByGuideOn = (a, b) => a.isOn === b.isOn; - const sidebarSelector = state => ({ [LEFT_SIDEBAR_NAMES.JOBS]: selectors.jobs.count(state), [LEFT_SIDEBAR_NAMES.PIPELINES]: selectors.pipelines.collection.count(state), @@ -100,7 +98,7 @@ const Name = styled.span` const SidebarLeft = () => { const dataCountSource = useSelector(sidebarSelector, isEqual); - const { isOn } = useSelector(state => state.userGuide, equalByGuideOn); + const { isOn } = useSelector(selectors.userGuide); const location = useLocation(); const dataCount = isOn ? dataCountMock : dataCountSource; const { isCollapsed, toggle } = useLeftSidebar(); diff --git a/src/Routes/Base/UserGuide/index.js b/src/Routes/Base/UserGuide/index.js index a5ab66663..907d6e301 100644 --- a/src/Routes/Base/UserGuide/index.js +++ b/src/Routes/Base/UserGuide/index.js @@ -2,6 +2,7 @@ import React, { useCallback } from 'react'; import { useSelector, useDispatch } from 'react-redux'; import Joyride, { EVENTS, ACTIONS } from 'react-joyride'; import { useHistory } from 'react-router-dom'; +import { selectors } from 'reducers'; import { triggerUserGuide, changeStep as _changeStep, @@ -13,13 +14,11 @@ import UserGuideTooltip from './UserGuideTooltip.react'; const stepAction = [ACTIONS.NEXT, ACTIONS.PREV, ACTIONS.INIT, ACTIONS.UPDATE]; -const isOnEqual = (a, b) => a.isOn === b.isOn; - const UserGuide = () => { const history = useHistory(); const { setCollapsed } = useLeftSidebar(); - const { isOn } = useSelector(state => state.userGuide, isOnEqual); + const { isOn } = useSelector(selectors.userGuide); const dispatch = useDispatch(); const trigger = useCallback(() => dispatch(triggerUserGuide()), [dispatch]); diff --git a/src/Routes/SidebarRight/AddDataSource/index.js b/src/Routes/SidebarRight/AddDataSource/index.js index e46d21c08..a0e3cb34b 100644 --- a/src/Routes/SidebarRight/AddDataSource/index.js +++ b/src/Routes/SidebarRight/AddDataSource/index.js @@ -1,11 +1,12 @@ import React, { useCallback, useContext, useState } from 'react'; import PropTypes from 'prop-types'; -import { useDispatch } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import { Input, Icon, Form, Button, Alert } from 'antd'; import { BottomContent } from 'components/common'; import { createDataSource } from 'actions/dataSources'; import UploadDragger, { useDragger } from 'components/UploadDragger'; import { DRAWER_SIZE } from 'const'; +import { selectors } from 'reducers'; import ctx from './../ctx'; /** @type {import('antd/lib/upload/interface').UploadFile[]} */ @@ -15,12 +16,11 @@ const AddDataSource = ({ form }) => { const context = useContext(ctx); const { getFieldDecorator, validateFields } = form; const dispatch = useDispatch(); - const [addedFiles, setAddedFiles] = useState(initialState); const { onChange, customRequest } = useDragger({ setFileList: setAddedFiles, }); - + const SubmittingStatus = useSelector(selectors.dataSources.createStatus); const onSubmit = useCallback( e => { e.preventDefault(); @@ -67,8 +67,12 @@ const AddDataSource = ({ form }) => { - - diff --git a/src/Routes/Tables/DataSources/EditDrawer/Body/FileBrowser/stratifier.js b/src/Routes/Tables/DataSources/EditDrawer/Body/FileBrowser/stratifier.js index 9bff3b346..dc303946e 100644 --- a/src/Routes/Tables/DataSources/EditDrawer/Body/FileBrowser/stratifier.js +++ b/src/Routes/Tables/DataSources/EditDrawer/Body/FileBrowser/stratifier.js @@ -42,6 +42,17 @@ export const stratify = flatList => { */ let pathsMap = {}; + if (flatList.length === 0) + return { + '/': { + children: 0, + childrenIds: [], + id: '/', + isDir: true, + name: '/', + }, + }; + /** @type {StratifiedMap} */ const stratifiedMap = flatList.reduce((acc, file) => { /** @type {PathsMap} */ @@ -106,6 +117,7 @@ export const stratify = flatList => { }, }; }, {}); + return stratifiedMap; }; diff --git a/src/Routes/Tables/Pipelines/index.js b/src/Routes/Tables/Pipelines/index.js index f5530de50..4f453a846 100644 --- a/src/Routes/Tables/Pipelines/index.js +++ b/src/Routes/Tables/Pipelines/index.js @@ -20,7 +20,6 @@ const PipelinesTable = () => { }), [goTo] ); - return ( <> { dispatch, ]); - useEffect(() => { - reFetchAll(); - }, [reFetchAll]); - const fetchDataSource = useCallback( name => dispatch(actions.fetchDataSource({ name })), [dispatch] diff --git a/src/hooks/usePipeline.js b/src/hooks/usePipeline.js index c38b79ece..682065b13 100644 --- a/src/hooks/usePipeline.js +++ b/src/hooks/usePipeline.js @@ -1,13 +1,13 @@ import { useCallback } from 'react'; import { useSelector } from 'react-redux'; -import { selectors } from 'reducers/pipeline.reducer'; +import { selectors } from 'reducers'; import useActions from './useActions'; import { useFilter } from './useSearch'; const usePipeline = () => { - const collection = useSelector(selectors.collection.all); + const collection = useSelector(selectors.pipelines.collection.all); - const dataStats = useSelector(selectors.stats.all); + const dataStats = useSelector(selectors.pipelines.stats.all); const filtered = useFilter(collection, 'name'); const { updateStored } = useActions(); const updateCron = useCallback( diff --git a/src/hooks/useSearch.js b/src/hooks/useSearch.js index 67b692460..0cf31e462 100644 --- a/src/hooks/useSearch.js +++ b/src/hooks/useSearch.js @@ -1,12 +1,13 @@ import { useMemo } from 'react'; import { useSelector } from 'react-redux'; +import { selectors } from 'reducers'; /** * @template T * @type {(collection: T[], key: string) => T[]} */ export const useFilter = (collection, key) => { - const filter = useSelector(state => state.autoCompleteFilter.filter); + const filter = useSelector(selectors.autoCompleteFilter); const data = useMemo( () => collection.filter(item => item[key].includes(filter)), [collection, key, filter] diff --git a/src/reducers/dataSources/actionTypes.js b/src/reducers/dataSources/actionTypes.js index d3e3dc720..40db5513b 100644 --- a/src/reducers/dataSources/actionTypes.js +++ b/src/reducers/dataSources/actionTypes.js @@ -1,6 +1,6 @@ import actionTypes from './../../const/application-actions'; -export default { +const types = { fetchAll: { pending: `${actionTypes.DATASOURCE_FETCH_ALL}_PENDING`, success: `${actionTypes.DATASOURCE_FETCH_ALL}_SUCCESS`, @@ -37,3 +37,5 @@ export default { fail: `${actionTypes.SNAPSHOT_POST}_REJECT`, }, }; + +export default types; diff --git a/src/reducers/dataSources/datasource.d.ts b/src/reducers/dataSources/datasource.d.ts index 26f9b9a85..8dacfb3b2 100644 --- a/src/reducers/dataSources/datasource.d.ts +++ b/src/reducers/dataSources/datasource.d.ts @@ -1,3 +1,6 @@ +import types from './actionTypes'; + +export type AsyncType = typeof types.fetchAll; export type FetchStatus = 'SUCCESS' | 'PENDING' | 'FAIL' | 'IDLE'; export type FileMeta = { diff --git a/src/reducers/dataSources/index.js b/src/reducers/dataSources/index.js index edb2fc0be..895535148 100644 --- a/src/reducers/dataSources/index.js +++ b/src/reducers/dataSources/index.js @@ -18,6 +18,7 @@ export { snapshotsActions }; * @typedef {import('@reduxjs/toolkit').EntityState} CollectionState * @typedef {import('./datasource').DataSourceEntry} DataSourceEntry * @typedef {import('./datasource').FetchStatus} FetchStatus + * @typedef {import('./datasource').AsyncType} AsyncType * @typedef {import('./versions').VersionsState} VersionsState * @typedef {import('./snapshots').SnapshotsState} SnapshotsState * @typedef {{ @@ -25,6 +26,7 @@ export { snapshotsActions }; * collection: CollectionState; * snapshots: SnapshotsState; * status: FetchStatus; + * createStatus: FetchStatus; * error: string | null; * versions: VersionsState; * }; @@ -120,20 +122,27 @@ const dataSources = createSlice({ }, }); -/** @type {(state: FetchStatus, action: { type: string }) => FetchStatus} */ -const status = (state = 'IDLE', { type }) => { +/** + * @type {( + * asyncType: AsyncType + * ) => (state: FetchStatus, action: { type: string }) => FetchStatus} + */ +const statusReducer = asyncType => (state = 'IDLE', { type }) => { switch (type) { - case types.fetchAll.pending: + case asyncType.pending: return 'PENDING'; - case types.fetchAll.success: + case asyncType.success: return 'SUCCESS'; - case types.fetchAll.fail: + case asyncType.fail: return 'FAIL'; default: return state; } }; +const status = statusReducer(types.fetchAll); +const createStatus = statusReducer(types.create); + const error = (state = null, { type }) => { if (type !== types.fetchAll.fail) return state; // return different types of errors by payload @@ -145,6 +154,7 @@ export const reducer = combineReducers({ versions: versionsReducer, snapshots: snapshotsReducer, status, + createStatus, error, }); @@ -190,4 +200,6 @@ export const selectors = { /** @param {State} state */ snapshots: (state, dataSourceName) => state.dataSources.snapshots[dataSourceName], + /** @param {State} state */ + createStatus: state => state.dataSources.createStatus, };