From f21cf6672d61d290577ecddb237fa43582f55c48 Mon Sep 17 00:00:00 2001 From: Arnei Date: Thu, 4 Jan 2024 15:50:00 +0100 Subject: [PATCH 1/4] Modernize redux: jobSlice Switching to redux toolkit for getting jobs for the job table. --- app/src/actions/jobActions.ts | 35 ---------- app/src/components/shared/MainNav.tsx | 7 +- app/src/components/systems/Jobs.tsx | 16 ++--- app/src/components/systems/Servers.tsx | 9 ++- app/src/components/systems/Services.tsx | 9 ++- app/src/reducers/jobReducer.ts | 71 ------------------- app/src/selectors/jobSelectors.ts | 7 +- app/src/slices/jobSlice.ts | 90 +++++++++++++++++++++++++ app/src/store.ts | 2 +- app/src/thunks/jobThunks.ts | 27 -------- app/src/thunks/tableThunks.ts | 3 +- 11 files changed, 113 insertions(+), 163 deletions(-) delete mode 100644 app/src/actions/jobActions.ts delete mode 100644 app/src/reducers/jobReducer.ts create mode 100644 app/src/slices/jobSlice.ts delete mode 100644 app/src/thunks/jobThunks.ts diff --git a/app/src/actions/jobActions.ts b/app/src/actions/jobActions.ts deleted file mode 100644 index 0a83ace62c..0000000000 --- a/app/src/actions/jobActions.ts +++ /dev/null @@ -1,35 +0,0 @@ -/** - * This file contains all redux actions that can be executed on jobs - */ - -// Constants of action types for fetching jobs from server -export const LOAD_JOBS_IN_PROGRESS = "LOAD_JOBS_IN_PROGRESS"; -export const LOAD_JOBS_SUCCESS = "LOAD_JOBS_SUCCESS"; -export const LOAD_JOBS_FAILURE = "LOAD_JOBS_FAILURE"; - -// Constants of action types affecting UI -export const SET_JOB_COLUMNS = "SET_JOB_COLUMNS"; - -// Actions affecting fetching jobs from server - -export const loadJobsInProgress = () => ({ - type: LOAD_JOBS_IN_PROGRESS, -}); - -// @ts-expect-error TS(7006): Parameter 'jobs' implicitly has an 'any' type. -export const loadJobsSuccess = (jobs) => ({ - type: LOAD_JOBS_SUCCESS, - payload: { jobs }, -}); - -export const loadJobsFailure = () => ({ - type: LOAD_JOBS_FAILURE, -}); - -// Actions affecting UI - -// @ts-expect-error TS(7006): Parameter 'updatedColumns' implicitly has an 'any'... Remove this comment to see the full error message -export const setJobColumns = (updatedColumns) => ({ - type: SET_JOB_COLUMNS, - payload: { updatedColumns }, -}); diff --git a/app/src/components/shared/MainNav.tsx b/app/src/components/shared/MainNav.tsx index e75a72381b..6fe9c0d5ca 100644 --- a/app/src/components/shared/MainNav.tsx +++ b/app/src/components/shared/MainNav.tsx @@ -16,7 +16,6 @@ import { } from "../../thunks/tableThunks"; import { fetchEvents } from "../../thunks/eventThunks"; import { fetchRecordings } from "../../thunks/recordingThunks"; -import { fetchJobs } from "../../thunks/jobThunks"; import { fetchUsers } from "../../thunks/userThunks"; import { fetchThemes } from "../../thunks/themeThunks"; import { fetchFilters, fetchStats } from "../../thunks/tableFilterThunks"; @@ -31,6 +30,7 @@ import { GlobalHotKeys } from "react-hotkeys"; import { availableHotkeys } from "../../configs/hotkeysConfig"; import { fetchAcls } from "../../slices/aclSlice"; import { useAppDispatch } from "../../store"; +import { fetchJobs } from "../../slices/jobSlice"; /** * This component renders the main navigation that opens when the burger button is clicked @@ -54,8 +54,6 @@ const MainNav = ({ loadingRecordings, // @ts-expect-error TS(7031): Binding element 'loadingRecordingsIntoTable' impli... Remove this comment to see the full error message loadingRecordingsIntoTable, -// @ts-expect-error TS(7031): Binding element 'loadingJobs' implicitly has an 'a... Remove this comment to see the full error message - loadingJobs, // @ts-expect-error TS(7031): Binding element 'loadingJobsIntoTable' implicitly ... Remove this comment to see the full error message loadingJobsIntoTable, // @ts-expect-error TS(7031): Binding element 'loadingServers' implicitly has an... Remove this comment to see the full error message @@ -140,7 +138,7 @@ const MainNav = ({ resetOffset(); // Fetching jobs from server - loadingJobs(); + dispatch(fetchJobs()); // Load jobs into table loadingJobsIntoTable(); @@ -351,7 +349,6 @@ const mapDispatchToProps = (dispatch) => ({ // @ts-expect-error TS(2554): Expected 1 arguments, but got 0. loadingRecordings: () => dispatch(fetchRecordings()), loadingRecordingsIntoTable: () => dispatch(loadRecordingsIntoTable()), - loadingJobs: () => dispatch(fetchJobs()), loadingJobsIntoTable: () => dispatch(loadJobsIntoTable()), loadingServers: () => dispatch(fetchServers()), loadingServersIntoTable: () => dispatch(loadServersIntoTable()), diff --git a/app/src/components/systems/Jobs.tsx b/app/src/components/systems/Jobs.tsx index d6635ee521..b2c892781d 100644 --- a/app/src/components/systems/Jobs.tsx +++ b/app/src/components/systems/Jobs.tsx @@ -10,7 +10,6 @@ import Notifications from "../shared/Notifications"; import { jobsTemplateMap } from "../../configs/tableConfigs/jobsTableConfig"; import { getTotalJobs } from "../../selectors/jobSelectors"; import { fetchFilters } from "../../thunks/tableFilterThunks"; -import { fetchJobs } from "../../thunks/jobThunks"; import { loadJobsIntoTable, loadServersIntoTable, @@ -26,17 +25,15 @@ import Footer from "../Footer"; import { getUserInformation } from "../../selectors/userInfoSelectors"; import { hasAccess } from "../../utils/utils"; import { getCurrentFilterResource } from "../../selectors/tableFilterSelectors"; +import { useAppDispatch, useAppSelector } from "../../store"; +import { fetchJobs } from "../../slices/jobSlice"; /** * This component renders the table view of jobs */ const Jobs = ({ -// @ts-expect-error TS(7031): Binding element 'loadingJobs' implicitly has an 'a... Remove this comment to see the full error message - loadingJobs, // @ts-expect-error TS(7031): Binding element 'loadingJobsIntoTable' implicitly ... Remove this comment to see the full error message loadingJobsIntoTable, -// @ts-expect-error TS(7031): Binding element 'jobs' implicitly has an 'any' typ... Remove this comment to see the full error message - jobs, // @ts-expect-error TS(7031): Binding element 'loadingFilters' implicitly has an... Remove this comment to see the full error message loadingFilters, // @ts-expect-error TS(7031): Binding element 'loadingServers' implicitly has an... Remove this comment to see the full error message @@ -57,11 +54,14 @@ const Jobs = ({ currentFilterType, }) => { const { t } = useTranslation(); + const dispatch = useAppDispatch(); const [displayNavigation, setNavigation] = useState(false); + const jobs = useAppSelector(state => getTotalJobs(state)); + const loadJobs = async () => { // Fetching jobs from server - await loadingJobs(); + await dispatch(fetchJobs()); // Load jobs into table loadingJobsIntoTable(); @@ -158,7 +158,7 @@ const Jobs = ({
{/* Include filters component */} @@ -176,7 +176,6 @@ const Jobs = ({ // Getting state data out of redux store // @ts-expect-error TS(7006): Parameter 'state' implicitly has an 'any' type. const mapStateToProps = (state) => ({ - jobs: getTotalJobs(state), user: getUserInformation(state), currentFilterType: getCurrentFilterResource(state), }); @@ -186,7 +185,6 @@ const mapStateToProps = (state) => ({ const mapDispatchToProps = (dispatch) => ({ // @ts-expect-error TS(7006): Parameter 'resource' implicitly has an 'any' type. loadingFilters: (resource) => dispatch(fetchFilters(resource)), - loadingJobs: () => dispatch(fetchJobs()), loadingJobsIntoTable: () => dispatch(loadJobsIntoTable()), loadingServers: () => dispatch(fetchServers()), loadingServersIntoTable: () => dispatch(loadServersIntoTable()), diff --git a/app/src/components/systems/Servers.tsx b/app/src/components/systems/Servers.tsx index 13e6453046..771475f62c 100644 --- a/app/src/components/systems/Servers.tsx +++ b/app/src/components/systems/Servers.tsx @@ -16,7 +16,6 @@ import { loadServersIntoTable, loadServicesIntoTable, } from "../../thunks/tableThunks"; -import { fetchJobs } from "../../thunks/jobThunks"; import { fetchServices } from "../../thunks/serviceThunks"; import { editTextFilter } from "../../actions/tableFilterActions"; import { setOffset } from "../../actions/tableActions"; @@ -26,6 +25,8 @@ import Footer from "../Footer"; import { getUserInformation } from "../../selectors/userInfoSelectors"; import { hasAccess } from "../../utils/utils"; import { getCurrentFilterResource } from "../../selectors/tableFilterSelectors"; +import { useAppDispatch } from "../../store"; +import { fetchJobs } from "../../slices/jobSlice"; /** * This component renders the table view of servers @@ -39,8 +40,6 @@ const Servers = ({ servers, // @ts-expect-error TS(7031): Binding element 'loadingFilters' implicitly has an... Remove this comment to see the full error message loadingFilters, -// @ts-expect-error TS(7031): Binding element 'loadingJobs' implicitly has an 'a... Remove this comment to see the full error message - loadingJobs, // @ts-expect-error TS(7031): Binding element 'loadingJobsIntoTable' implicitly ... Remove this comment to see the full error message loadingJobsIntoTable, // @ts-expect-error TS(7031): Binding element 'loadingServices' implicitly has a... Remove this comment to see the full error message @@ -57,6 +56,7 @@ const Servers = ({ currentFilterType, }) => { const { t } = useTranslation(); + const dispatch = useAppDispatch(); const [displayNavigation, setNavigation] = useState(false); const loadServers = async () => { @@ -72,7 +72,7 @@ const Servers = ({ resetOffset(); // Fetching jobs from server - loadingJobs(); + dispatch(fetchJobs()); // Load jobs into table loadingJobsIntoTable(); @@ -188,7 +188,6 @@ const mapDispatchToProps = (dispatch) => ({ loadingFilters: (resource) => dispatch(fetchFilters(resource)), loadingServers: () => dispatch(fetchServers()), loadingServersIntoTable: () => dispatch(loadServersIntoTable()), - loadingJobs: () => dispatch(fetchJobs()), loadingJobsIntoTable: () => dispatch(loadJobsIntoTable()), loadingServices: () => dispatch(fetchServices()), loadingServicesIntoTable: () => dispatch(loadServicesIntoTable()), diff --git a/app/src/components/systems/Services.tsx b/app/src/components/systems/Services.tsx index a4172bd829..46bd300473 100644 --- a/app/src/components/systems/Services.tsx +++ b/app/src/components/systems/Services.tsx @@ -9,7 +9,6 @@ import MainNav from "../shared/MainNav"; import Notifications from "../shared/Notifications"; import { servicesTemplateMap } from "../../configs/tableConfigs/servicesTableConfig"; import { fetchFilters } from "../../thunks/tableFilterThunks"; -import { fetchJobs } from "../../thunks/jobThunks"; import { loadJobsIntoTable, loadServersIntoTable, @@ -26,6 +25,8 @@ import Footer from "../Footer"; import { getUserInformation } from "../../selectors/userInfoSelectors"; import { hasAccess } from "../../utils/utils"; import { getCurrentFilterResource } from "../../selectors/tableFilterSelectors"; +import { useAppDispatch } from "../../store"; +import { fetchJobs } from "../../slices/jobSlice"; /** * This component renders the table view of services @@ -39,8 +40,6 @@ const Services = ({ services, // @ts-expect-error TS(7031): Binding element 'loadingFilters' implicitly has an... Remove this comment to see the full error message loadingFilters, -// @ts-expect-error TS(7031): Binding element 'loadingJobs' implicitly has an 'a... Remove this comment to see the full error message - loadingJobs, // @ts-expect-error TS(7031): Binding element 'loadingJobsIntoTable' implicitly ... Remove this comment to see the full error message loadingJobsIntoTable, // @ts-expect-error TS(7031): Binding element 'loadingServers' implicitly has an... Remove this comment to see the full error message @@ -57,6 +56,7 @@ const Services = ({ currentFilterType, }) => { const { t } = useTranslation(); + const dispatch = useAppDispatch(); const [displayNavigation, setNavigation] = useState(false); const loadServices = async () => { @@ -72,7 +72,7 @@ const Services = ({ resetOffset(); // Fetching jobs from server - loadingJobs(); + dispatch(fetchJobs()); // Load jobs into table loadingJobsIntoTable(); @@ -188,7 +188,6 @@ const mapDispatchToProps = (dispatch) => ({ loadingFilters: (resource) => dispatch(fetchFilters(resource)), loadingServices: () => dispatch(fetchServices()), loadingServicesIntoTable: () => dispatch(loadServicesIntoTable()), - loadingJobs: () => dispatch(fetchJobs()), loadingJobsIntoTable: () => dispatch(loadJobsIntoTable()), loadingServers: () => dispatch(fetchServers()), loadingServersIntoTable: () => dispatch(loadServersIntoTable()), diff --git a/app/src/reducers/jobReducer.ts b/app/src/reducers/jobReducer.ts deleted file mode 100644 index 7595db394b..0000000000 --- a/app/src/reducers/jobReducer.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { jobsTableConfig } from "../configs/tableConfigs/jobsTableConfig"; -import { - LOAD_JOBS_FAILURE, - LOAD_JOBS_IN_PROGRESS, - LOAD_JOBS_SUCCESS, - SET_JOB_COLUMNS, -} from "../actions/jobActions"; - -/** - * This file contains redux reducer for actions affecting the state of jobs - */ - -// Fill columns initially with columns defined in jobsTableConfig -const initialColumns = jobsTableConfig.columns.map((column) => ({ - ...column, - deactivated: false, -})); - -// Initial state of jobs in redux store -const initialState = { - isLoading: false, - results: [], - columns: initialColumns, - total: 0, - count: 0, - offset: 0, - limit: 0, -}; - -// Reducer for jobs -// @ts-expect-error TS(7006): Parameter 'action' implicitly has an 'any' type. -const jobs = (state = initialState, action) => { - const { type, payload } = action; - switch (type) { - case LOAD_JOBS_IN_PROGRESS: { - return { - ...state, - isLoading: true, - }; - } - case LOAD_JOBS_SUCCESS: { - const { jobs } = payload; - return { - ...state, - isLoading: false, - total: jobs.total, - count: jobs.count, - limit: jobs.limit, - offset: jobs.offset, - results: jobs.results, - }; - } - case LOAD_JOBS_FAILURE: { - return { - ...state, - isLoading: false, - }; - } - case SET_JOB_COLUMNS: { - const { updatedColumns } = payload; - return { - ...state, - columns: updatedColumns, - }; - } - default: - return state; - } -}; - -export default jobs; diff --git a/app/src/selectors/jobSelectors.ts b/app/src/selectors/jobSelectors.ts index 1d412273eb..503833a48b 100644 --- a/app/src/selectors/jobSelectors.ts +++ b/app/src/selectors/jobSelectors.ts @@ -1,6 +1,7 @@ +import { RootState } from "../store"; + /** * This file contains selectors regarding jobs */ - -export const getJobs = (state: any) => state.jobs.results; -export const getTotalJobs = (state: any) => state.jobs.total; +export const getJobs = (state: RootState) => state.jobs.results; +export const getTotalJobs = (state: RootState) => state.jobs.total; diff --git a/app/src/slices/jobSlice.ts b/app/src/slices/jobSlice.ts new file mode 100644 index 0000000000..f695fccf5d --- /dev/null +++ b/app/src/slices/jobSlice.ts @@ -0,0 +1,90 @@ +import { PayloadAction, SerializedError, createAsyncThunk, createSlice } from '@reduxjs/toolkit' +import { jobsTableConfig } from '../configs/tableConfigs/jobsTableConfig'; +import axios from 'axios'; +import { getURLParams } from '../utils/resourceUtils'; + +/** + * This file contains redux reducer for actions affecting the state of jobs + */ +type JobState = { + status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + error: SerializedError | null, + results: any[], // TODO: proper typing + columns: any, // TODO: proper typing, derive from `initialColumns` + total: number, + count: number, + offset: number, + limit: number, +} + +// Fill columns initially with columns defined in jobsTableConfig +const initialColumns = jobsTableConfig.columns.map((column) => ({ + ...column, + deactivated: false, +})); + +// Initial state of jobs in redux store +const initialState: JobState = { + status: 'uninitialized', + error: null, + results: [], + columns: initialColumns, + total: 0, + count: 0, + offset: 0, + limit: 0, +}; + +export const fetchJobs = createAsyncThunk('jobs/fetchJobs', async (_, { getState }) => { + const state = getState(); + let params = getURLParams(state); + // Just make the async request here, and return the response. + // This will automatically dispatch a `pending` action first, + // and then `fulfilled` or `rejected` actions based on the promise. + // /jobs.json?limit=0&offset=0&filter={filter}&sort={sort} + const res = await axios.get("/admin-ng/job/jobs.json?n", { params: params }); + return res.data; +}); + +const jobSlice = createSlice({ + name: 'jobs', + initialState, + reducers: { + setJobColumns(state, action: PayloadAction<{ + updatedColumns: JobState["columns"], + }>) { + state.columns = action.payload.updatedColumns; + }, + }, + // These are used for thunks + extraReducers: builder => { + builder + .addCase(fetchJobs.pending, (state) => { + state.status = 'loading'; + }) + .addCase(fetchJobs.fulfilled, (state, action: PayloadAction<{ + total: JobState["total"], + count: JobState["count"], + limit: JobState["limit"], + offset: JobState["offset"], + results: JobState["results"], + }>) => { + state.status = 'succeeded'; + const jobs = action.payload; + state.total = jobs.total; + state.count = jobs.count; + state.limit = jobs.limit; + state.offset = jobs.offset; + state.results = jobs.results; + }) + .addCase(fetchJobs.rejected, (state, action) => { + state.status = 'failed'; + state.error = action.error; + }); + } +}); + +export const { setJobColumns } = jobSlice.actions; + +// Export the slice reducer as the default export +export default jobSlice.reducer; diff --git a/app/src/store.ts b/app/src/store.ts index 790807712c..08b42af8b8 100644 --- a/app/src/store.ts +++ b/app/src/store.ts @@ -9,7 +9,7 @@ import events from "./reducers/eventReducers"; import table from "./reducers/tableReducers"; import series from "./reducers/seriesReducer"; import recordings from "./reducers/recordingReducer"; -import jobs from "./reducers/jobReducer"; +import jobs from "./slices/jobSlice"; import servers from "./reducers/serverReducer"; import services from "./reducers/serviceReducer"; import users from "./reducers/userReducers"; diff --git a/app/src/thunks/jobThunks.ts b/app/src/thunks/jobThunks.ts deleted file mode 100644 index f405d17ec7..0000000000 --- a/app/src/thunks/jobThunks.ts +++ /dev/null @@ -1,27 +0,0 @@ -import axios from "axios"; -import { - loadJobsFailure, - loadJobsInProgress, - loadJobsSuccess, -} from "../actions/jobActions"; -import { getURLParams } from "../utils/resourceUtils"; - -// fetch jobs from server -// @ts-expect-error TS(7006): Parameter 'dispatch' implicitly has an 'any' type. -export const fetchJobs = () => async (dispatch, getState) => { - try { - dispatch(loadJobsInProgress()); - - const state = getState(); - let params = getURLParams(state); - - // /jobs.json?limit=0&offset=0&filter={filter}&sort={sort} - let data = await axios.get("/admin-ng/job/jobs.json?", { params: params }); - - const jobs = await data.data; - dispatch(loadJobsSuccess(jobs)); - } catch (e) { - console.error(e); - dispatch(loadJobsFailure()); - } -}; diff --git a/app/src/thunks/tableThunks.ts b/app/src/thunks/tableThunks.ts index 3220b05dde..b097edc77a 100644 --- a/app/src/thunks/tableThunks.ts +++ b/app/src/thunks/tableThunks.ts @@ -35,14 +35,13 @@ import { import { fetchEvents } from "./eventThunks"; import { fetchSeries } from "./seriesThunks"; import { fetchRecordings } from "./recordingThunks"; -import { fetchJobs } from "./jobThunks"; +import { fetchJobs, setJobColumns } from "../slices/jobSlice"; import { fetchServers } from "./serverThunks"; import { fetchServices } from "./serviceThunks"; import { fetchUsers } from "./userThunks"; import { fetchGroups } from "./groupThunks"; import { fetchThemes } from "./themeThunks"; import { setRecordingsColumns } from "../actions/recordingActions"; -import { setJobColumns } from "../actions/jobActions"; import { setServerColumns } from "../actions/serverActions"; import { setUserColumns } from "../actions/userActions"; import { setGroupColumns } from "../actions/groupActions"; From 4cfa09fdb44087be4cbbbe13e6c5eb82bb08c627 Mon Sep 17 00:00:00 2001 From: Arnei Date: Mon, 12 Feb 2024 15:01:16 +0100 Subject: [PATCH 2/4] Add typing for job result Adds typing on what we expect the backend to return for jobs. --- app/src/slices/jobSlice.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/app/src/slices/jobSlice.ts b/app/src/slices/jobSlice.ts index f695fccf5d..e58d548f4d 100644 --- a/app/src/slices/jobSlice.ts +++ b/app/src/slices/jobSlice.ts @@ -6,10 +6,22 @@ import { getURLParams } from '../utils/resourceUtils'; /** * This file contains redux reducer for actions affecting the state of jobs */ +type Job = { + creator: string, + id: number, + operation: string, + processingHost: string, + processingNode: string, + started: string, + status: string, + submitted: string, + type: string, +} + type JobState = { status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', error: SerializedError | null, - results: any[], // TODO: proper typing + results: Job[], columns: any, // TODO: proper typing, derive from `initialColumns` total: number, count: number, @@ -42,7 +54,7 @@ export const fetchJobs = createAsyncThunk('jobs/fetchJobs', async (_, { getState // This will automatically dispatch a `pending` action first, // and then `fulfilled` or `rejected` actions based on the promise. // /jobs.json?limit=0&offset=0&filter={filter}&sort={sort} - const res = await axios.get("/admin-ng/job/jobs.json?n", { params: params }); + const res = await axios.get("/admin-ng/job/jobs.json?", { params: params }); return res.data; }); From 1aa52aa2e4fe01a253a16bdb7b014497a7eeb8b2 Mon Sep 17 00:00:00 2001 From: Arnei Date: Wed, 14 Feb 2024 16:39:33 +0100 Subject: [PATCH 3/4] Fix job table crashing when changing columns --- app/src/slices/jobSlice.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/slices/jobSlice.ts b/app/src/slices/jobSlice.ts index e58d548f4d..92bdb6a546 100644 --- a/app/src/slices/jobSlice.ts +++ b/app/src/slices/jobSlice.ts @@ -62,10 +62,10 @@ const jobSlice = createSlice({ name: 'jobs', initialState, reducers: { - setJobColumns(state, action: PayloadAction<{ - updatedColumns: JobState["columns"], - }>) { - state.columns = action.payload.updatedColumns; + setJobColumns(state, action: PayloadAction< + JobState["columns"] + >) { + state.columns = action.payload; }, }, // These are used for thunks From 18d19ae90e94d46fc3e3e437803ac02092064779 Mon Sep 17 00:00:00 2001 From: Arnei Date: Wed, 14 Feb 2024 16:40:53 +0100 Subject: [PATCH 4/4] Fix table filters in jobs table --- app/src/components/systems/Jobs.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/src/components/systems/Jobs.tsx b/app/src/components/systems/Jobs.tsx index b2c892781d..d4de220e92 100644 --- a/app/src/components/systems/Jobs.tsx +++ b/app/src/components/systems/Jobs.tsx @@ -59,6 +59,11 @@ const Jobs = ({ const jobs = useAppSelector(state => getTotalJobs(state)); + // TODO: Get rid of the wrappers when modernizing redux is done + const fetchJobsWrapper = () => { + dispatch(fetchJobs()) + } + const loadJobs = async () => { // Fetching jobs from server await dispatch(fetchJobs()); @@ -158,7 +163,7 @@ const Jobs = ({
{/* Include filters component */}