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 1f00135b41..da8d097df5 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 "../../slices/eventSlice";
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
@@ -52,8 +52,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
@@ -138,7 +136,7 @@ const MainNav = ({
resetOffset();
// Fetching jobs from server
- loadingJobs();
+ dispatch(fetchJobs());
// Load jobs into table
loadingJobsIntoTable();
@@ -348,7 +346,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..d4de220e92 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,19 @@ const Jobs = ({
currentFilterType,
}) => {
const { t } = useTranslation();
+ const dispatch = useAppDispatch();
const [displayNavigation, setNavigation] = useState(false);
+ 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 loadingJobs();
+ await dispatch(fetchJobs());
// Load jobs into table
loadingJobsIntoTable();
@@ -158,7 +163,7 @@ const Jobs = ({
{/* Include filters component */}
@@ -176,7 +181,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 +190,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..92bdb6a546
--- /dev/null
+++ b/app/src/slices/jobSlice.ts
@@ -0,0 +1,102 @@
+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 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: Job[],
+ 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?", { params: params });
+ return res.data;
+});
+
+const jobSlice = createSlice({
+ name: 'jobs',
+ initialState,
+ reducers: {
+ setJobColumns(state, action: PayloadAction<
+ JobState["columns"]
+ >) {
+ state.columns = action.payload;
+ },
+ },
+ // 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 5e621a954c..37df5882b5 100644
--- a/app/src/store.ts
+++ b/app/src/store.ts
@@ -9,7 +9,7 @@ import events from "./slices/eventSlice";
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 47fcd4d04c..76b13dfea7 100644
--- a/app/src/thunks/tableThunks.ts
+++ b/app/src/thunks/tableThunks.ts
@@ -35,14 +35,13 @@ import {
} from "../selectors/tableSelectors";
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";