From 3aa9592b795496f751cfc62417498b98b9a90a3e Mon Sep 17 00:00:00 2001 From: Arnei Date: Tue, 19 Dec 2023 16:19:23 +0100 Subject: [PATCH 1/8] Fix addNotification([...]) typing Turns out even more of the parameters for the addNotification function in the notificationThunks are perfectly optional. --- app/src/thunks/aclDetailsThunks.ts | 2 -- app/src/thunks/aclThunks.ts | 4 ---- app/src/thunks/eventDetailsThunks.ts | 2 -- app/src/thunks/eventThunks.ts | 17 ----------------- app/src/thunks/groupDetailsThunks.ts | 3 --- app/src/thunks/groupThunks.ts | 5 ----- app/src/thunks/notificationThunks.ts | 2 +- app/src/thunks/recordingThunks.ts | 3 --- app/src/thunks/seriesThunks.ts | 6 ------ app/src/thunks/taskThunks.ts | 2 -- app/src/thunks/themeDetailsThunks.ts | 2 -- app/src/thunks/themeThunks.ts | 4 ---- app/src/thunks/userDetailsThunks.ts | 2 -- app/src/thunks/userInfoThunks.ts | 1 - app/src/thunks/userThunks.ts | 4 ---- 15 files changed, 1 insertion(+), 58 deletions(-) diff --git a/app/src/thunks/aclDetailsThunks.ts b/app/src/thunks/aclDetailsThunks.ts index 75025f78b6..f5642605a2 100644 --- a/app/src/thunks/aclDetailsThunks.ts +++ b/app/src/thunks/aclDetailsThunks.ts @@ -121,12 +121,10 @@ export const updateAclDetails = (values, aclId) => async (dispatch) => { .put(`/admin-ng/acl/${aclId}`, data) .then((response) => { console.info(response); -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("success", "ACL_UPDATED")); }) .catch((response) => { console.error(response); -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("error", "ACL_NOT_SAVED")); }); }; diff --git a/app/src/thunks/aclThunks.ts b/app/src/thunks/aclThunks.ts index 4a505a0bba..c8126be65e 100644 --- a/app/src/thunks/aclThunks.ts +++ b/app/src/thunks/aclThunks.ts @@ -69,12 +69,10 @@ export const postNewAcl = (values) => async (dispatch) => { }) .then((response) => { console.info(response); -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("success", "ACL_ADDED")); }) .catch((response) => { console.error(response); -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("error", "ACL_NOT_SAVED")); }); }; @@ -86,13 +84,11 @@ export const deleteAcl = (id) => async (dispatch) => { .then((res) => { console.info(res); //add success notification -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("success", "ACL_DELETED")); }) .catch((res) => { console.error(res); // add error notification -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("error", "ACL_NOT_DELETED")); }); }; diff --git a/app/src/thunks/eventDetailsThunks.ts b/app/src/thunks/eventDetailsThunks.ts index ac3239cb39..e1de916d59 100644 --- a/app/src/thunks/eventDetailsThunks.ts +++ b/app/src/thunks/eventDetailsThunks.ts @@ -578,14 +578,12 @@ export const updateAssets = (values, eventId) => async (dispatch, getState) => { .then((response) => { console.info(response); dispatch( -// @ts-expect-error TS(2554): Expected 5 arguments, but got 4. addNotification("success", "EVENTS_UPDATED", null, NOTIFICATION_CONTEXT) ); }) .catch((response) => { console.error(response); dispatch( -// @ts-expect-error TS(2554): Expected 5 arguments, but got 4. addNotification( "error", "EVENTS_NOT_UPDATED", diff --git a/app/src/thunks/eventThunks.ts b/app/src/thunks/eventThunks.ts index e9623e2e72..23b853d392 100644 --- a/app/src/thunks/eventThunks.ts +++ b/app/src/thunks/eventThunks.ts @@ -184,7 +184,6 @@ export const updateBulkMetadata = (metadataFields, values) => async ( .then((res) => { console.info(res); dispatch( -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. addNotification("success", "BULK_METADATA_UPDATE.ALL_EVENTS_UPDATED") ); }) @@ -196,19 +195,16 @@ export const updateBulkMetadata = (metadataFields, values) => async ( // if this error data is undefined then an unexpected error occurred if (!err.data) { dispatch( -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. addNotification("error", "BULK_METADATA_UPDATE.UNEXPECTED_ERROR") ); } else { if (err.data.updated && err.data.updated.length === 0) { dispatch( -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. addNotification("error", "BULK_METADATA_UPDATE.NO_EVENTS_UPDATED") ); } if (err.data.updateFailures && err.data.updateFailures.length > 0) { dispatch( -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. addNotification( "warning", "BULK_METADATA_UPDATE.SOME_EVENTS_NOT_UPDATED" @@ -217,7 +213,6 @@ export const updateBulkMetadata = (metadataFields, values) => async ( } if (err.data.notFound && err.data.notFound.length > 0) { dispatch( -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. addNotification( "warning", "BULK_ACTIONS.EDIT_EVENTS_METADATA.REQUEST_ERRORS.NOT_FOUND" @@ -227,7 +222,6 @@ export const updateBulkMetadata = (metadataFields, values) => async ( } } else { dispatch( -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. addNotification("error", "BULK_METADATA_UPDATE.UNEXPECTED_ERROR") ); } @@ -485,12 +479,10 @@ export const postNewEvent = (values, metadataInfo, extendedMetadata) => async ( .post("/admin-ng/event/new", formData, config) .then((response) => { console.info(response); -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("success", "EVENTS_CREATED")); }) .catch((response) => { console.error(response); -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("error", "EVENTS_NOT_CREATED")); }); }; @@ -504,20 +496,16 @@ export const deleteEvent = (id) => async (dispatch) => { .then((res) => { // add success notification depending on status code if (res.status === 200) { -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("success", "EVENT_DELETED")); } else { -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("success", "EVENT_WILL_BE_DELETED")); } }) .catch((res) => { // add error notification depending on status code if (res.status === 401) { -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("error", "EVENTS_NOT_DELETED_NOT_AUTHORIZED")); } else { -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("error", "EVENTS_NOT_DELETED")); } }); @@ -539,13 +527,11 @@ export const deleteMultipleEvent = (events) => async (dispatch) => { .then((res) => { console.info(res); //add success notification -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("success", "EVENTS_DELETED")); }) .catch((res) => { console.error(res); //add error notification -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("error", "EVENTS_NOT_DELETED")); }); }; @@ -706,7 +692,6 @@ export const updateScheduledEventsBulk = (values) => async (dispatch) => { if (!eventChanges || !originalEvent) { dispatch( -// @ts-expect-error TS(2554): Expected 5 arguments, but got 4. addNotification( "error", "EVENTS_NOT_UPDATED_ID", @@ -772,12 +757,10 @@ export const updateScheduledEventsBulk = (values) => async (dispatch) => { .put("/admin-ng/event/bulk/update", formData) .then((res) => { console.info(res); -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("success", "EVENTS_UPDATED_ALL")); }) .catch((res) => { console.error(res); -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("error", "EVENTS_NOT_UPDATED_ALL")); }); }; diff --git a/app/src/thunks/groupDetailsThunks.ts b/app/src/thunks/groupDetailsThunks.ts index acb54b1b0e..db772da1dc 100644 --- a/app/src/thunks/groupDetailsThunks.ts +++ b/app/src/thunks/groupDetailsThunks.ts @@ -55,16 +55,13 @@ export const updateGroupDetails = (values, groupId) => async (dispatch) => { .put(`/admin-ng/groups/${groupId}`, data) .then((response) => { console.info(response); -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("success", "GROUP_UPDATED")); }) .catch((response) => { console.error(response); if (response.status === 409) { -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("error", "GROUP_CONFLICT")); } else { -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("error", "GROUP_NOT_SAVED")); } }); diff --git a/app/src/thunks/groupThunks.ts b/app/src/thunks/groupThunks.ts index be9a9e88c2..c42f9ec97e 100644 --- a/app/src/thunks/groupThunks.ts +++ b/app/src/thunks/groupThunks.ts @@ -43,16 +43,13 @@ export const postNewGroup = (values) => async (dispatch) => { }) .then((response) => { console.info(response); -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("success", "GROUP_ADDED")); }) .catch((response) => { console.error(response); if (response.status === 409) { -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("error", "GROUP_CONFLICT")); } else { -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("error", "GROUP_NOT_SAVED")); } }); @@ -66,13 +63,11 @@ export const deleteGroup = (id) => async (dispatch) => { .then((res) => { console.info(res); // add success notification -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("success", "GROUP_DELETED")); }) .catch((res) => { console.error(res); // add error notification -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("error", "GROUP_NOT_DELETED")); }); }; diff --git a/app/src/thunks/notificationThunks.ts b/app/src/thunks/notificationThunks.ts index 2267a9e601..96cbe65d91 100644 --- a/app/src/thunks/notificationThunks.ts +++ b/app/src/thunks/notificationThunks.ts @@ -11,7 +11,7 @@ import { } from "../configs/generalConfig"; // @ts-expect-error TS(7006): Parameter 'dispatch' implicitly has an 'any' type. -export const addNotification = (type, key, duration, parameter, context, id?) => ( +export const addNotification = (type, key, duration?, parameter?, context?, id?) => ( // @ts-expect-error TS(7006): Parameter 'dispatch' implicitly has an 'any' type. dispatch, // @ts-expect-error TS(7006): Parameter 'getState' implicitly has an 'any' type. diff --git a/app/src/thunks/recordingThunks.ts b/app/src/thunks/recordingThunks.ts index f08584ecd8..cd1d7ddc37 100644 --- a/app/src/thunks/recordingThunks.ts +++ b/app/src/thunks/recordingThunks.ts @@ -67,7 +67,6 @@ export const deleteRecording = (id) => async (dispatch) => { .then((res) => { console.info(res); // add success notification -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("success", "LOCATION_DELETED")); }) .catch((res) => { @@ -75,11 +74,9 @@ export const deleteRecording = (id) => async (dispatch) => { // add error notification depending on status code if (res.status === 401) { dispatch( -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. addNotification("error", "LOCATION_NOT_DELETED_NOT_AUTHORIZED") ); } else { -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("error", "LOCATION_NOT_DELETED")); } }); diff --git a/app/src/thunks/seriesThunks.ts b/app/src/thunks/seriesThunks.ts index 6065ef99a8..304c215767 100644 --- a/app/src/thunks/seriesThunks.ts +++ b/app/src/thunks/seriesThunks.ts @@ -155,12 +155,10 @@ export const postNewSeries = (values, metadataInfo, extendedMetadata) => async ( }) .then((response) => { console.info(response); -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("success", "SERIES_ADDED")); }) .catch((response) => { console.error(response); -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("error", "SERIES_NOT_SAVED")); }); }; @@ -195,13 +193,11 @@ export const deleteSeries = (id) => async (dispatch) => { .then((res) => { console.info(res); // add success notification -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("success", "SERIES_DELETED")); }) .catch((res) => { console.error(res); // add error notification -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("error", "SERIES_NOT_DELETED")); }); }; @@ -222,13 +218,11 @@ export const deleteMultipleSeries = (series) => async (dispatch) => { .then((res) => { console.info(res); //add success notification -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("success", "SERIES_DELETED")); }) .catch((res) => { console.error(res); //add error notification -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("error", "SERIES_NOT_DELETED")); }); }; diff --git a/app/src/thunks/taskThunks.ts b/app/src/thunks/taskThunks.ts index fc7b0bf58c..a2e76fc79d 100644 --- a/app/src/thunks/taskThunks.ts +++ b/app/src/thunks/taskThunks.ts @@ -33,12 +33,10 @@ export const postTasks = (values: any) => async (dispatch: any) => { }) .then((response) => { console.info(response); -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("success", "TASK_CREATED")); }) .catch((response) => { console.error(response); -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("error", "TASK_NOT_CREATED")); }); }; diff --git a/app/src/thunks/themeDetailsThunks.ts b/app/src/thunks/themeDetailsThunks.ts index 878f7cedec..3a67d0a8ca 100644 --- a/app/src/thunks/themeDetailsThunks.ts +++ b/app/src/thunks/themeDetailsThunks.ts @@ -56,12 +56,10 @@ export const updateThemeDetails = (id, values) => async (dispatch) => { }) .then((response) => { console.info(response); -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("success", "THEME_CREATED")); }) .catch((response) => { console.error(response); -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("error", "THEME_NOT_CREATED")); }); }; diff --git a/app/src/thunks/themeThunks.ts b/app/src/thunks/themeThunks.ts index 177f97bd91..c7288ebc48 100644 --- a/app/src/thunks/themeThunks.ts +++ b/app/src/thunks/themeThunks.ts @@ -42,12 +42,10 @@ export const postNewTheme = (values) => async (dispatch) => { }) .then((response) => { console.info(response); -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("success", "THEME_CREATED")); }) .catch((response) => { console.error(response); -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("error", "THEME_NOT_CREATED")); }); }; @@ -59,13 +57,11 @@ export const deleteTheme = (id) => async (dispatch) => { .then((res) => { console.info(res); // add success notification -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("success", "THEME_DELETED")); }) .catch((res) => { console.error(res); // add error notification -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("error", "THEME_NOT_DELETED")); }); }; diff --git a/app/src/thunks/userDetailsThunks.ts b/app/src/thunks/userDetailsThunks.ts index 77318c3eba..8d6d44928b 100644 --- a/app/src/thunks/userDetailsThunks.ts +++ b/app/src/thunks/userDetailsThunks.ts @@ -40,12 +40,10 @@ export const updateUserDetails = (values, username) => async (dispatch) => { .put(`/admin-ng/users/${username}.json`, data) .then((response) => { console.info(response); -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("success", "USER_UPDATED")); }) .catch((response) => { console.error(response); -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("error", "USER_NOT_SAVED")); }); }; diff --git a/app/src/thunks/userInfoThunks.ts b/app/src/thunks/userInfoThunks.ts index 3511319b59..be4ceadca7 100644 --- a/app/src/thunks/userInfoThunks.ts +++ b/app/src/thunks/userInfoThunks.ts @@ -29,7 +29,6 @@ export const fetchUserInfo = () => async (dispatch) => { } catch (e) { console.error(e); dispatch(loadUserInfoFailure()); -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("error", "PROBLEM_ON_START")); } }; diff --git a/app/src/thunks/userThunks.ts b/app/src/thunks/userThunks.ts index 77ee5e5402..9ac6033c04 100644 --- a/app/src/thunks/userThunks.ts +++ b/app/src/thunks/userThunks.ts @@ -55,12 +55,10 @@ export const postNewUser = (values) => async (dispatch) => { }) .then((response) => { console.info(response); -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("success", "USER_ADDED")); }) .catch((response) => { console.error(response); -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("error", "USER_NOT_SAVED")); }); }; @@ -74,13 +72,11 @@ export const deleteUser = (id) => async (dispatch) => { .then((res) => { console.info(res); // add success notification -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("success", "USER_DELETED")); }) .catch((res) => { console.error(res); // add error notification -// @ts-expect-error TS(2554): Expected 5 arguments, but got 2. dispatch(addNotification("error", "USER_NOT_DELETED")); }); }; From 6e4dcac7c0207917b076f92b49a80ecb3e86c877 Mon Sep 17 00:00:00 2001 From: Arnei Date: Thu, 21 Dec 2023 10:07:19 +0100 Subject: [PATCH 2/8] Move seriesTableMap into its own file Fixes a "can't access lexical declaration 'X' before initialization" error, by resolving a kind of circular dependency. --- app/src/components/events/Series.tsx | 2 +- .../configs/tableConfigs/seriesTableConfig.ts | 18 ------------------ app/src/configs/tableConfigs/seriesTableMap.ts | 17 +++++++++++++++++ 3 files changed, 18 insertions(+), 19 deletions(-) create mode 100644 app/src/configs/tableConfigs/seriesTableMap.ts diff --git a/app/src/components/events/Series.tsx b/app/src/components/events/Series.tsx index 228992d917..112babbdc8 100644 --- a/app/src/components/events/Series.tsx +++ b/app/src/components/events/Series.tsx @@ -9,7 +9,7 @@ import Table from "../shared/Table"; import Notifications from "../shared/Notifications"; import NewResourceModal from "../shared/NewResourceModal"; import DeleteSeriesModal from "./partials/modals/DeleteSeriesModal"; -import { seriesTemplateMap } from "../../configs/tableConfigs/seriesTableConfig"; +import { seriesTemplateMap } from "../../configs/tableConfigs/seriesTableMap"; import { fetchSeries, fetchSeriesMetadata, diff --git a/app/src/configs/tableConfigs/seriesTableConfig.ts b/app/src/configs/tableConfigs/seriesTableConfig.ts index 54e2596483..26613cd122 100644 --- a/app/src/configs/tableConfigs/seriesTableConfig.ts +++ b/app/src/configs/tableConfigs/seriesTableConfig.ts @@ -1,9 +1,3 @@ -import SeriesTitleCell from "../../components/events/partials/SeriesTitleCell"; -import SeriesCreatorsCell from "../../components/events/partials/SeriesCreatorsCell"; -import SeriesContributorsCell from "../../components/events/partials/SeriesContributorsCell"; -import SeriesDateTimeCell from "../../components/events/partials/SeriesDateTimeCell"; -import SeriesActionsCell from "../../components/events/partials/SeriesActionsCell"; - /** * Config that contains the columns and further information regarding series. These are the information that never or hardly changes. * That's why it is hard coded here and not fetched from server. @@ -51,15 +45,3 @@ export const seriesTableConfig = { category: "events", multiSelect: true, }; - -/** - * This map contains the mapping between the template strings above and the corresponding react component. - * This helps to render different templates of cells more dynamically - */ -export const seriesTemplateMap = { - SeriesTitleCell: SeriesTitleCell, - SeriesCreatorsCell: SeriesCreatorsCell, - SeriesContributorsCell: SeriesContributorsCell, - SeriesDateTimeCell: SeriesDateTimeCell, - SeriesActionsCell: SeriesActionsCell, -}; diff --git a/app/src/configs/tableConfigs/seriesTableMap.ts b/app/src/configs/tableConfigs/seriesTableMap.ts new file mode 100644 index 0000000000..88a113035f --- /dev/null +++ b/app/src/configs/tableConfigs/seriesTableMap.ts @@ -0,0 +1,17 @@ +import SeriesTitleCell from "../../components/events/partials/SeriesTitleCell"; +import SeriesCreatorsCell from "../../components/events/partials/SeriesCreatorsCell"; +import SeriesContributorsCell from "../../components/events/partials/SeriesContributorsCell"; +import SeriesDateTimeCell from "../../components/events/partials/SeriesDateTimeCell"; +import SeriesActionsCell from "../../components/events/partials/SeriesActionsCell"; + +/** + * This map contains the mapping between the template strings above and the corresponding react component. + * This helps to render different templates of cells more dynamically + */ +export const seriesTemplateMap = { + SeriesTitleCell: SeriesTitleCell, + SeriesCreatorsCell: SeriesCreatorsCell, + SeriesContributorsCell: SeriesContributorsCell, + SeriesDateTimeCell: SeriesDateTimeCell, + SeriesActionsCell: SeriesActionsCell, +}; From 464401ef34a941ec57b8edf2b5657e1e8c658426 Mon Sep 17 00:00:00 2001 From: Arnei Date: Fri, 5 Jan 2024 16:35:17 +0100 Subject: [PATCH 3/8] Modernize redux: seriesSlice Switching to redux toolkit for getting series for the series table. --- app/src/actions/seriesActions.ts | 99 ----- app/src/components/events/Events.tsx | 9 +- app/src/components/events/Series.tsx | 46 +-- .../ModalTabsAndPages/NewThemePage.tsx | 12 +- .../events/partials/SeriesActionsCell.tsx | 31 +- .../partials/modals/DeleteSeriesModal.tsx | 12 +- .../partials/wizards/NewSeriesSummary.tsx | 21 +- .../partials/wizards/NewSeriesWizard.tsx | 38 +- app/src/components/shared/MainNav.tsx | 7 +- app/src/reducers/seriesReducer.ts | 159 -------- app/src/selectors/seriesSeletctor.ts | 23 +- app/src/slices/seriesSlice.ts | 373 ++++++++++++++++++ app/src/store.ts | 2 +- app/src/thunks/eventThunks.ts | 2 +- app/src/thunks/seriesThunks.ts | 259 ------------ app/src/thunks/serviceThunks.ts | 4 +- app/src/thunks/tableThunks.ts | 22 +- 17 files changed, 460 insertions(+), 659 deletions(-) delete mode 100644 app/src/actions/seriesActions.ts delete mode 100644 app/src/reducers/seriesReducer.ts create mode 100644 app/src/slices/seriesSlice.ts delete mode 100644 app/src/thunks/seriesThunks.ts diff --git a/app/src/actions/seriesActions.ts b/app/src/actions/seriesActions.ts deleted file mode 100644 index 50a2220f82..0000000000 --- a/app/src/actions/seriesActions.ts +++ /dev/null @@ -1,99 +0,0 @@ -/** - * This file contains all redux actions that can be executed on series - */ - -// Constants of actions types for fetching series from server -export const LOAD_SERIES_IN_PROGRESS = "LOAD_SERIES_IN_PROGRESS"; -export const LOAD_SERIES_SUCCESS = "LOAD_SERIES_SUCCESS"; -export const LOAD_SERIES_FAILURE = "LOAD_SERIES_FAILURE"; - -// Constant of actions types affecting UI -export const SHOW_ACTIONS_SERIES = "SHOW_ACTIONS_SERIES"; -export const SET_SERIES_COLUMNS = "SET_SERIES_COLUMNS"; -export const SET_SERIES_SELECTED = "SET_SERIES_SELECTED"; -export const SET_SERIES_DELETION_ALLOWED = "SET_SERIES_DELETION_ALLOWED"; - -// Constants of action types affecting fetching of series metadata from server -export const LOAD_SERIES_METADATA_IN_PROGRESS = - "LOAD_SERIES_METADATA_IN_PROGRESS"; -export const LOAD_SERIES_METADATA_SUCCESS = "LOAD_SERIES_METADATA_SUCCESS"; -export const LOAD_SERIES_METADATA_FAILURE = "LOAD_SERIES_METADATA_FAILURE"; - -// Constants of action types affecting fetching of series themes from server -export const LOAD_SERIES_THEMES_IN_PROGRESS = "LOAD_SERIES_THEMES_IN_PROGRESS"; -export const LOAD_SERIES_THEMES_SUCCESS = "LOAD_SERIES_THEMES_SUCCESS"; -export const LOAD_SERIES_THEMES_FAILURE = "LOAD_SERIES_THEMES_FAILURE"; - -// Actions affecting fetching series from server - -export const loadSeriesInProgress = () => ({ - type: LOAD_SERIES_IN_PROGRESS, -}); - -// @ts-expect-error TS(7006): Parameter 'series' implicitly has an 'any' type. -export const loadSeriesSuccess = (series) => ({ - type: LOAD_SERIES_SUCCESS, - payload: { series }, -}); - -export const loadSeriesFailure = () => ({ - type: LOAD_SERIES_FAILURE, -}); - -// Actions affecting UI - -// @ts-expect-error TS(7006): Parameter 'isShowing' implicitly has an 'any' type... Remove this comment to see the full error message -export const showActions = (isShowing) => ({ - type: SHOW_ACTIONS_SERIES, - payload: { isShowing }, -}); - -// @ts-expect-error TS(7006): Parameter 'updatedColumns' implicitly has an 'any'... Remove this comment to see the full error message -export const setSeriesColumns = (updatedColumns) => ({ - type: SET_SERIES_COLUMNS, - payload: { updatedColumns }, -}); - -// @ts-expect-error TS(7006): Parameter 'id' implicitly has an 'any' type. -export const setSeriesSelected = (id) => ({ - type: SET_SERIES_SELECTED, - payload: { id }, -}); - -// @ts-expect-error TS(7006): Parameter 'deletionAllowed' implicitly has an 'any... Remove this comment to see the full error message -export const setSeriesDeletionAllowed = (deletionAllowed, hasEvents) => ({ - type: SET_SERIES_DELETION_ALLOWED, - payload: { deletionAllowed, hasEvents }, -}); - -// Actions affecting fetching of series metadata from server - -export const loadSeriesMetadataInProgress = () => ({ - type: LOAD_SERIES_IN_PROGRESS, -}); - -// @ts-expect-error TS(7006): Parameter 'metadata' implicitly has an 'any' type. -export const loadSeriesMetadataSuccess = (metadata, extendedMetadata) => ({ - type: LOAD_SERIES_METADATA_SUCCESS, - payload: { metadata, extendedMetadata }, -}); - -export const loadSeriesMetadataFailure = () => ({ - type: LOAD_SERIES_METADATA_FAILURE, -}); - -// Actions affecting fetching of series themes from server - -export const loadSeriesThemesInProgress = () => ({ - type: LOAD_SERIES_THEMES_IN_PROGRESS, -}); - -// @ts-expect-error TS(7006): Parameter 'themes' implicitly has an 'any' type. -export const loadSeriesThemesSuccess = (themes) => ({ - type: LOAD_SERIES_THEMES_SUCCESS, - payload: { themes }, -}); - -export const loadSeriesThemesFailure = () => ({ - type: LOAD_SERIES_THEMES_FAILURE, -}); diff --git a/app/src/components/events/Events.tsx b/app/src/components/events/Events.tsx index a90dcdffe9..a0bbc388c6 100644 --- a/app/src/components/events/Events.tsx +++ b/app/src/components/events/Events.tsx @@ -19,7 +19,6 @@ import { loadEventsIntoTable, loadSeriesIntoTable, } from "../../thunks/tableThunks"; -import { fetchSeries } from "../../thunks/seriesThunks"; import { fetchFilters, fetchStats } from "../../thunks/tableFilterThunks"; import { getTotalEvents, @@ -39,6 +38,8 @@ import { GlobalHotKeys } from "react-hotkeys"; import { availableHotkeys } from "../../configs/hotkeysConfig"; import { getCurrentFilterResource } from "../../selectors/tableFilterSelectors"; import { fetchAssetUploadOptions } from "../../thunks/assetsThunks"; +import { useAppDispatch } from "../../store"; +import { fetchSeries } from "../../slices/seriesSlice"; // References for detecting a click outside of the container of the dropdown menu const containerAction = React.createRef(); @@ -55,8 +56,6 @@ const Events = ({ events, // @ts-expect-error TS(7031): Binding element 'showActions' implicitly has an 'a... Remove this comment to see the full error message showActions, -// @ts-expect-error TS(7031): Binding element 'loadingSeries' implicitly has an ... Remove this comment to see the full error message - loadingSeries, // @ts-expect-error TS(7031): Binding element 'loadingSeriesIntoTable' implicitl... Remove this comment to see the full error message loadingSeriesIntoTable, // @ts-expect-error TS(7031): Binding element 'loadingFilters' implicitly has an... Remove this comment to see the full error message @@ -81,6 +80,7 @@ const Events = ({ currentFilterType, }) => { const { t } = useTranslation(); + const dispatch = useAppDispatch(); const [displayActionMenu, setActionMenu] = useState(false); const [displayNavigation, setNavigation] = useState(false); const [displayNewEventModal, setNewEventModal] = useState(false); @@ -112,7 +112,7 @@ const Events = ({ resetOffset(); //fetching series from server - loadingSeries(); + dispatch(fetchSeries()); //load series into table loadingSeriesIntoTable(); @@ -358,7 +358,6 @@ const mapStateToProps = (state) => ({ const mapDispatchToProps = (dispatch) => ({ loadingEvents: () => dispatch(fetchEvents()), loadingEventsIntoTable: () => dispatch(loadEventsIntoTable()), - loadingSeries: () => dispatch(fetchSeries()), loadingSeriesIntoTable: () => dispatch(loadSeriesIntoTable()), // @ts-expect-error TS(7006): Parameter 'resource' implicitly has an 'any' type. loadingFilters: (resource) => dispatch(fetchFilters(resource)), diff --git a/app/src/components/events/Series.tsx b/app/src/components/events/Series.tsx index 112babbdc8..7c35b9478e 100644 --- a/app/src/components/events/Series.tsx +++ b/app/src/components/events/Series.tsx @@ -10,11 +10,6 @@ import Notifications from "../shared/Notifications"; import NewResourceModal from "../shared/NewResourceModal"; import DeleteSeriesModal from "./partials/modals/DeleteSeriesModal"; import { seriesTemplateMap } from "../../configs/tableConfigs/seriesTableMap"; -import { - fetchSeries, - fetchSeriesMetadata, - fetchSeriesThemes, -} from "../../thunks/seriesThunks"; import { loadEventsIntoTable, loadSeriesIntoTable, @@ -29,10 +24,16 @@ import Header from "../Header"; import Footer from "../Footer"; import { getUserInformation } from "../../selectors/userInfoSelectors"; import { hasAccess } from "../../utils/utils"; -import { showActions } from "../../actions/seriesActions"; import { availableHotkeys } from "../../configs/hotkeysConfig"; import { GlobalHotKeys } from "react-hotkeys"; import { getCurrentFilterResource } from "../../selectors/tableFilterSelectors"; +import { useAppDispatch, useAppSelector } from "../../store"; +import { + fetchSeries, + fetchSeriesMetadata, + fetchSeriesThemes, + showActionsSeries, +} from "../../slices/seriesSlice"; // References for detecting a click outside of the container of the dropdown menu const containerAction = React.createRef(); @@ -41,38 +42,27 @@ const containerAction = React.createRef(); * This component renders the table view of series */ const Series = ({ -// @ts-expect-error TS(7031): Binding element 'showActions' implicitly has an 'a... Remove this comment to see the full error message - showActions, -// @ts-expect-error TS(7031): Binding element 'loadingSeries' implicitly has an ... Remove this comment to see the full error message - loadingSeries, // @ts-expect-error TS(7031): Binding element 'loadingSeriesIntoTable' implicitl... Remove this comment to see the full error message loadingSeriesIntoTable, // @ts-expect-error TS(7031): Binding element 'loadingEvents' implicitly has an ... Remove this comment to see the full error message loadingEvents, // @ts-expect-error TS(7031): Binding element 'loadingEventsIntoTable' implicitl... Remove this comment to see the full error message loadingEventsIntoTable, -// @ts-expect-error TS(7031): Binding element 'series' implicitly has an 'any' t... Remove this comment to see the full error message - series, // @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 'loadingStats' implicitly has an '... Remove this comment to see the full error message loadingStats, -// @ts-expect-error TS(7031): Binding element 'loadingSeriesMetadata' implicitly... Remove this comment to see the full error message - loadingSeriesMetadata, -// @ts-expect-error TS(7031): Binding element 'loadingSeriesThemes' implicitly h... Remove this comment to see the full error message - loadingSeriesThemes, // @ts-expect-error TS(7031): Binding element 'resetTextFilter' implicitly has a... Remove this comment to see the full error message resetTextFilter, // @ts-expect-error TS(7031): Binding element 'resetOffset' implicitly has an 'a... Remove this comment to see the full error message resetOffset, // @ts-expect-error TS(7031): Binding element 'user' implicitly has an 'any' typ... Remove this comment to see the full error message user, -// @ts-expect-error TS(7031): Binding element 'setShowActions' implicitly has an... Remove this comment to see the full error message - setShowActions, // @ts-expect-error TS(7031): Binding element 'currentFilterType' implicitly has... Remove this comment to see the full error message currentFilterType, }) => { const { t } = useTranslation(); + const dispatch = useAppDispatch(); const [displayActionMenu, setActionMenu] = useState(false); const [displayNavigation, setNavigation] = useState(false); const [displayNewSeriesModal, setNewSeriesModal] = useState(false); @@ -80,6 +70,9 @@ const Series = ({ let location = useLocation(); + const series = useAppSelector(state => getTotalSeries(state)); + const showActions = useAppSelector(state => isShowActions(state)); + const loadEvents = () => { // Reset the current page to first page resetOffset(); @@ -96,7 +89,7 @@ const Series = ({ const loadSeries = async () => { //fetching series from server - await loadingSeries(); + await dispatch(fetchSeries()); //load series into table loadingSeriesIntoTable(); @@ -110,7 +103,7 @@ const Series = ({ resetTextFilter(); // disable actions button - setShowActions(false); + dispatch(showActionsSeries(false)); // Load events on mount loadSeries().then((r) => console.info(r)); @@ -151,8 +144,8 @@ const Series = ({ }; const showNewSeriesModal = async () => { - await loadingSeriesMetadata(); - await loadingSeriesThemes(); + await dispatch(fetchSeriesMetadata()); + await dispatch(fetchSeriesThemes()); setNewSeriesModal(true); }; @@ -254,7 +247,7 @@ const Series = ({ {/* Include filters component */} @@ -273,8 +266,6 @@ const Series = ({ // Getting state data out of redux store // @ts-expect-error TS(7006): Parameter 'state' implicitly has an 'any' type. const mapStateToProps = (state) => ({ - series: getTotalSeries(state), - showActions: isShowActions(state), user: getUserInformation(state), currentFilterType: getCurrentFilterResource(state), }); @@ -282,19 +273,14 @@ const mapStateToProps = (state) => ({ // Mapping actions to dispatch // @ts-expect-error TS(7006): Parameter 'dispatch' implicitly has an 'any' type. const mapDispatchToProps = (dispatch) => ({ - loadingSeries: () => dispatch(fetchSeries()), loadingSeriesIntoTable: () => dispatch(loadSeriesIntoTable()), loadingEvents: () => dispatch(fetchEvents()), loadingEventsIntoTable: () => dispatch(loadEventsIntoTable()), // @ts-expect-error TS(7006): Parameter 'resource' implicitly has an 'any' type. loadingFilters: (resource) => dispatch(fetchFilters(resource)), loadingStats: () => dispatch(fetchStats()), - loadingSeriesMetadata: () => dispatch(fetchSeriesMetadata()), - loadingSeriesThemes: () => dispatch(fetchSeriesThemes()), resetTextFilter: () => dispatch(editTextFilter("")), resetOffset: () => dispatch(setOffset(0)), -// @ts-expect-error TS(7006): Parameter 'isShowing' implicitly has an 'any' type... Remove this comment to see the full error message - setShowActions: (isShowing) => dispatch(showActions(isShowing)), }); export default connect(mapStateToProps, mapDispatchToProps)(Series); diff --git a/app/src/components/events/partials/ModalTabsAndPages/NewThemePage.tsx b/app/src/components/events/partials/ModalTabsAndPages/NewThemePage.tsx index 2b464fc0af..dc754842ed 100644 --- a/app/src/components/events/partials/ModalTabsAndPages/NewThemePage.tsx +++ b/app/src/components/events/partials/ModalTabsAndPages/NewThemePage.tsx @@ -1,9 +1,9 @@ import React from "react"; import { useTranslation } from "react-i18next"; -import { connect } from "react-redux"; import { getSeriesThemes } from "../../../../selectors/seriesSeletctor"; import WizardNavigationButtons from "../../../shared/wizard/WizardNavigationButtons"; import DropDown from "../../../shared/DropDown"; +import { useAppSelector } from "../../../../store"; /** * This component renders the theme page for new series in the new series wizard. @@ -12,10 +12,11 @@ const NewThemePage = ({ formik, nextPage, previousPage, - seriesThemes }: any) => { const { t } = useTranslation(); + const seriesThemes = useAppSelector(state => getSeriesThemes(state)); + // @ts-expect-error TS(7006): Parameter 'id' implicitly has an 'any' type. const getDescription = (id) => { // @ts-expect-error TS(7006): Parameter 'theme' implicitly has an 'any' type. @@ -92,9 +93,4 @@ const NewThemePage = ({ ); }; -// @ts-expect-error TS(7006): Parameter 'state' implicitly has an 'any' type. -const mapStateToProps = (state) => ({ - seriesThemes: getSeriesThemes(state), -}); - -export default connect(mapStateToProps)(NewThemePage); +export default NewThemePage; diff --git a/app/src/components/events/partials/SeriesActionsCell.tsx b/app/src/components/events/partials/SeriesActionsCell.tsx index 8d0bc55c98..ff6f2ceef2 100644 --- a/app/src/components/events/partials/SeriesActionsCell.tsx +++ b/app/src/components/events/partials/SeriesActionsCell.tsx @@ -1,10 +1,6 @@ import React, { useState } from "react"; import { useTranslation } from "react-i18next"; import ConfirmModal from "../../shared/ConfirmModal"; -import { - checkForEventsDeleteSeriesModal, - deleteSeries, -} from "../../../thunks/seriesThunks"; import { connect } from "react-redux"; import SeriesDetailsModal from "./modals/SeriesDetailsModal"; import { @@ -20,6 +16,11 @@ import { getSeriesHasEvents, isSeriesDeleteAllowed, } from "../../../selectors/seriesSeletctor"; +import { useAppDispatch, useAppSelector } from "../../../store"; +import { + checkForEventsDeleteSeriesModal, + deleteSeries, +} from "../../../slices/seriesSlice"; /** * This component renders the action cells of series in the table view @@ -27,14 +28,10 @@ import { const SeriesActionsCell = ({ // @ts-expect-error TS(7031): Binding element 'row' implicitly has an 'any' type... Remove this comment to see the full error message row, -// @ts-expect-error TS(7031): Binding element 'deleteSeries' implicitly has an '... Remove this comment to see the full error message - deleteSeries, // @ts-expect-error TS(7031): Binding element 'fetchSeriesDetailsMetadata' impli... Remove this comment to see the full error message fetchSeriesDetailsMetadata, // @ts-expect-error TS(7031): Binding element 'fetchSeriesDetailsAcls' implicitl... Remove this comment to see the full error message fetchSeriesDetailsAcls, -// @ts-expect-error TS(7031): Binding element 'checkDeleteAllowed' implicitly ha... Remove this comment to see the full error message - checkDeleteAllowed, // @ts-expect-error TS(7031): Binding element 'fetchSeriesDetailsFeeds' implicit... Remove this comment to see the full error message fetchSeriesDetailsFeeds, // @ts-expect-error TS(7031): Binding element 'fetchSeriesDetailsTheme' implicit... Remove this comment to see the full error message @@ -43,29 +40,29 @@ const SeriesActionsCell = ({ fetchSeriesDetailsThemeNames, // @ts-expect-error TS(7031): Binding element 'user' implicitly has an 'any' typ... Remove this comment to see the full error message user, -// @ts-expect-error TS(7031): Binding element 'deleteAllowed' implicitly has an ... Remove this comment to see the full error message - deleteAllowed, -// @ts-expect-error TS(7031): Binding element 'hasEvents' implicitly has an 'any... Remove this comment to see the full error message - hasEvents, }) => { const { t } = useTranslation(); + const dispatch = useAppDispatch(); const [displayDeleteConfirmation, setDeleteConfirmation] = useState(false); const [displaySeriesDetailsModal, setSeriesDetailsModal] = useState(false); + const hasEvents = useAppSelector(state => getSeriesHasEvents(state)); + const deleteAllowed = useAppSelector(state => isSeriesDeleteAllowed(state)); + const hideDeleteConfirmation = () => { setDeleteConfirmation(false); }; const showDeleteConfirmation = async () => { - await checkDeleteAllowed(row.id); + await dispatch(checkForEventsDeleteSeriesModal(row.id)); setDeleteConfirmation(true); }; // @ts-expect-error TS(7006): Parameter 'id' implicitly has an 'any' type. const deletingSeries = (id) => { - deleteSeries(id); + dispatch(deleteSeries(id)); }; const hideSeriesDetailsModal = () => { @@ -137,14 +134,10 @@ const SeriesActionsCell = ({ // @ts-expect-error TS(7006): Parameter 'state' implicitly has an 'any' type. const mapStateToProps = (state) => ({ user: getUserInformation(state), - deleteAllowed: isSeriesDeleteAllowed(state), - hasEvents: getSeriesHasEvents(state), }); // @ts-expect-error TS(7006): Parameter 'dispatch' implicitly has an 'any' type. const mapDispatchToProps = (dispatch) => ({ -// @ts-expect-error TS(7006): Parameter 'id' implicitly has an 'any' type. - deleteSeries: (id) => dispatch(deleteSeries(id)), // @ts-expect-error TS(7006): Parameter 'id' implicitly has an 'any' type. fetchSeriesDetailsMetadata: (id) => dispatch(fetchSeriesDetailsMetadata(id)), // @ts-expect-error TS(7006): Parameter 'id' implicitly has an 'any' type. @@ -154,8 +147,6 @@ const mapDispatchToProps = (dispatch) => ({ // @ts-expect-error TS(7006): Parameter 'id' implicitly has an 'any' type. fetchSeriesDetailsTheme: (id) => dispatch(fetchSeriesDetailsTheme(id)), fetchSeriesDetailsThemeNames: () => dispatch(fetchNamesOfPossibleThemes()), -// @ts-expect-error TS(7006): Parameter 'id' implicitly has an 'any' type. - checkDeleteAllowed: (id) => dispatch(checkForEventsDeleteSeriesModal(id)), }); export default connect(mapStateToProps, mapDispatchToProps)(SeriesActionsCell); diff --git a/app/src/components/events/partials/modals/DeleteSeriesModal.tsx b/app/src/components/events/partials/modals/DeleteSeriesModal.tsx index 623aa2a9f1..de78ffd8c1 100644 --- a/app/src/components/events/partials/modals/DeleteSeriesModal.tsx +++ b/app/src/components/events/partials/modals/DeleteSeriesModal.tsx @@ -3,18 +3,20 @@ import { useTranslation } from "react-i18next"; import { getSelectedRows } from "../../../../selectors/tableSelectors"; import { connect } from "react-redux"; import cn from "classnames"; +import { useAppDispatch } from "../../../../store"; import { deleteMultipleSeries, getSeriesConfig, hasEvents, -} from "../../../../thunks/seriesThunks"; +} from "../../../../slices/seriesSlice"; /** * This component manges the delete series bulk action */ // @ts-expect-error TS(7031): Binding element 'close' implicitly has an 'any' ty... Remove this comment to see the full error message -const DeleteSeriesModal = ({ close, selectedRows, deleteMultipleSeries }) => { +const DeleteSeriesModal = ({ close, selectedRows }) => { const { t } = useTranslation(); + const dispatch = useAppDispatch(); const [allChecked, setAllChecked] = useState(true); const [selectedSeries, setSelectedSeries] = useState(selectedRows); @@ -42,7 +44,7 @@ const DeleteSeriesModal = ({ close, selectedRows, deleteMultipleSeries }) => { }, []); const deleteSelectedSeries = () => { - deleteMultipleSeries(selectedSeries); + dispatch(deleteMultipleSeries(selectedSeries)); close(); }; @@ -230,9 +232,7 @@ const mapStateToProps = (state) => ({ // @ts-expect-error TS(7006): Parameter 'dispatch' implicitly has an 'any' type. const mapDispatchToProps = (dispatch) => ({ -// @ts-expect-error TS(7006): Parameter 'selectedSeries' implicitly has an 'any'... Remove this comment to see the full error message - deleteMultipleSeries: (selectedSeries) => - dispatch(deleteMultipleSeries(selectedSeries)), + }); export default connect(mapStateToProps, mapDispatchToProps)(DeleteSeriesModal); diff --git a/app/src/components/events/partials/wizards/NewSeriesSummary.tsx b/app/src/components/events/partials/wizards/NewSeriesSummary.tsx index 86b28ca6c5..6aaa899f7a 100644 --- a/app/src/components/events/partials/wizards/NewSeriesSummary.tsx +++ b/app/src/components/events/partials/wizards/NewSeriesSummary.tsx @@ -10,6 +10,7 @@ import MetadataSummaryTable from "./summaryTables/MetadataSummaryTable"; import MetadataExtendedSummaryTable from "./summaryTables/MetadataExtendedSummaryTable"; import AccessSummaryTable from "./summaryTables/AccessSummaryTable"; import WizardNavigationButtons from "../../../shared/wizard/WizardNavigationButtons"; +import { useAppSelector } from "../../../../store"; /** * This component renders the summary page for new series in the new series wizard. @@ -21,15 +22,13 @@ const NewSeriesSummary = ({ previousPage, // @ts-expect-error TS(7031): Binding element 'metaDataExtendedHidden' implicitl... Remove this comment to see the full error message metaDataExtendedHidden, -// @ts-expect-error TS(7031): Binding element 'metadataSeries' implicitly has an... Remove this comment to see the full error message - metadataSeries, -// @ts-expect-error TS(7031): Binding element 'extendedMetadata' implicitly has ... Remove this comment to see the full error message - extendedMetadata, -// @ts-expect-error TS(7031): Binding element 'seriesThemes' implicitly has an '... Remove this comment to see the full error message - seriesThemes, }) => { const { t } = useTranslation(); + const metadataSeries = useAppSelector(state => getSeriesMetadata(state)); + const extendedMetadata = useAppSelector(state => getSeriesExtendedMetadata(state)); + const seriesThemes = useAppSelector(state => getSeriesThemes(state)); + // Get additional information about chosen series theme // @ts-expect-error TS(7006): Parameter 'theme' implicitly has an 'any' type. const theme = seriesThemes.find((theme) => theme.id === formik.values.theme); @@ -92,12 +91,4 @@ const NewSeriesSummary = ({ ); }; -// Getting state data out of redux store -// @ts-expect-error TS(7006): Parameter 'state' implicitly has an 'any' type. -const mapStateToProps = (state) => ({ - metadataSeries: getSeriesMetadata(state), - extendedMetadata: getSeriesExtendedMetadata(state), - seriesThemes: getSeriesThemes(state), -}); - -export default connect(mapStateToProps)(NewSeriesSummary); +export default NewSeriesSummary; diff --git a/app/src/components/events/partials/wizards/NewSeriesWizard.tsx b/app/src/components/events/partials/wizards/NewSeriesWizard.tsx index a5f2e2f8e1..c0557bb7ed 100644 --- a/app/src/components/events/partials/wizards/NewSeriesWizard.tsx +++ b/app/src/components/events/partials/wizards/NewSeriesWizard.tsx @@ -6,29 +6,29 @@ import { getSeriesExtendedMetadata, getSeriesMetadata, } from "../../../../selectors/seriesSeletctor"; -import { connect } from "react-redux"; import NewMetadataPage from "../ModalTabsAndPages/NewMetadataPage"; import NewMetadataExtendedPage from "../ModalTabsAndPages/NewMetadataExtendedPage"; import NewAccessPage from "../ModalTabsAndPages/NewAccessPage"; -import { postNewSeries } from "../../../../thunks/seriesThunks"; import WizardStepper from "../../../shared/wizard/WizardStepper"; import { initialFormValuesNewSeries } from "../../../../configs/modalConfig"; import { NewSeriesSchema } from "../../../../utils/validate"; import { getInitialMetadataFieldValues } from "../../../../utils/resourceUtils"; +import { useAppDispatch, useAppSelector } from "../../../../store"; +import { postNewSeries } from "../../../../slices/seriesSlice"; /** * This component manages the pages of the new series wizard and the submission of values */ -const NewSeriesWizard = ({ -// @ts-expect-error TS(7031): Binding element 'metadataFields' implicitly has an... Remove this comment to see the full error message - metadataFields, -// @ts-expect-error TS(7031): Binding element 'extendedMetadata' implicitly has ... Remove this comment to see the full error message - extendedMetadata, -// @ts-expect-error TS(7031): Binding element 'close' implicitly has an 'any' ty... Remove this comment to see the full error message +const NewSeriesWizard: React.FC<{ + close: () => void +}> = ({ close, -// @ts-expect-error TS(7031): Binding element 'postNewSeries' implicitly has an ... Remove this comment to see the full error message - postNewSeries, }) => { + const dispatch = useAppDispatch(); + + const metadataFields = useAppSelector(state => getSeriesMetadata(state)); + const extendedMetadata = useAppSelector(state => getSeriesExtendedMetadata(state)); + const initialValues = getInitialValues(metadataFields, extendedMetadata); const [page, setPage] = useState(0); @@ -93,7 +93,7 @@ const NewSeriesWizard = ({ // @ts-expect-error TS(7006): Parameter 'values' implicitly has an 'any' type. const handleSubmit = (values) => { - const response = postNewSeries(values, metadataFields, extendedMetadata); + const response = dispatch(postNewSeries({values, metadataInfo: metadataFields, extendedMetadata})); console.info(response); close(); }; @@ -191,18 +191,4 @@ const getInitialValues = (metadataFields, extendedMetadata) => { return initialValues; }; -// Getting state data out of redux store -// @ts-expect-error TS(7006): Parameter 'state' implicitly has an 'any' type. -const mapStateToProps = (state) => ({ - metadataFields: getSeriesMetadata(state), - extendedMetadata: getSeriesExtendedMetadata(state), -}); - -// @ts-expect-error TS(7006): Parameter 'dispatch' implicitly has an 'any' type. -const mapDispatchToProps = (dispatch) => ({ -// @ts-expect-error TS(7006): Parameter 'values' implicitly has an 'any' type. - postNewSeries: (values, metadataFields, extendedMetadata) => - dispatch(postNewSeries(values, metadataFields, extendedMetadata)), -}); - -export default connect(mapStateToProps, mapDispatchToProps)(NewSeriesWizard); +export default NewSeriesWizard; diff --git a/app/src/components/shared/MainNav.tsx b/app/src/components/shared/MainNav.tsx index e75a72381b..b9eeda7b42 100644 --- a/app/src/components/shared/MainNav.tsx +++ b/app/src/components/shared/MainNav.tsx @@ -23,7 +23,6 @@ import { fetchFilters, fetchStats } from "../../thunks/tableFilterThunks"; import { setOffset } from "../../actions/tableActions"; import { getUserInformation } from "../../selectors/userInfoSelectors"; import { hasAccess } from "../../utils/utils"; -import { fetchSeries } from "../../thunks/seriesThunks"; import { fetchServers } from "../../thunks/serverThunks"; import { fetchServices } from "../../thunks/serviceThunks"; import { fetchGroups } from "../../thunks/groupThunks"; @@ -31,6 +30,7 @@ import { GlobalHotKeys } from "react-hotkeys"; import { availableHotkeys } from "../../configs/hotkeysConfig"; import { fetchAcls } from "../../slices/aclSlice"; import { useAppDispatch } from "../../store"; +import { fetchSeries } from "../../slices/seriesSlice"; /** * This component renders the main navigation that opens when the burger button is clicked @@ -44,8 +44,6 @@ const MainNav = ({ loadingEvents, // @ts-expect-error TS(7031): Binding element 'loadingEventsIntoTable' implicitl... Remove this comment to see the full error message loadingEventsIntoTable, -// @ts-expect-error TS(7031): Binding element 'loadingSeries' implicitly has an ... Remove this comment to see the full error message - loadingSeries, // @ts-expect-error TS(7031): Binding element 'loadingSeriesIntoTable' implicitl... Remove this comment to see the full error message loadingSeriesIntoTable, // @ts-expect-error TS(7031): Binding element 'loadingStats' implicitly has an '... Remove this comment to see the full error message @@ -114,7 +112,7 @@ const MainNav = ({ resetOffset(); // Fetching series from server - loadingSeries(); + dispatch(fetchSeries()); // Load series into table loadingSeriesIntoTable(); @@ -345,7 +343,6 @@ const mapStateToProps = (state) => ({ const mapDispatchToProps = (dispatch) => ({ loadingEvents: () => dispatch(fetchEvents()), loadingEventsIntoTable: () => dispatch(loadEventsIntoTable()), - loadingSeries: () => dispatch(fetchSeries()), loadingSeriesIntoTable: () => dispatch(loadSeriesIntoTable()), loadingStats: () => dispatch(fetchStats()), // @ts-expect-error TS(2554): Expected 1 arguments, but got 0. diff --git a/app/src/reducers/seriesReducer.ts b/app/src/reducers/seriesReducer.ts deleted file mode 100644 index a82cfc64b6..0000000000 --- a/app/src/reducers/seriesReducer.ts +++ /dev/null @@ -1,159 +0,0 @@ -import { seriesTableConfig } from "../configs/tableConfigs/seriesTableConfig"; -import { - LOAD_SERIES_FAILURE, - LOAD_SERIES_IN_PROGRESS, - LOAD_SERIES_METADATA_FAILURE, - LOAD_SERIES_METADATA_IN_PROGRESS, - LOAD_SERIES_METADATA_SUCCESS, - LOAD_SERIES_SUCCESS, - LOAD_SERIES_THEMES_FAILURE, - LOAD_SERIES_THEMES_IN_PROGRESS, - LOAD_SERIES_THEMES_SUCCESS, - SET_SERIES_COLUMNS, - SET_SERIES_DELETION_ALLOWED, - SET_SERIES_SELECTED, - SHOW_ACTIONS_SERIES, -} from "../actions/seriesActions"; - -/** - * This file contains redux reducer for actions affecting the state of series - */ - -// Fill columns initially with columns defined in seriesTableConfig -const initialColumns = seriesTableConfig.columns.map((column) => ({ - ...column, - deactivated: false, -})); - -// Initial state of series in redux store -const initialState = { - isLoading: false, - results: [], - columns: initialColumns, - showActions: false, - total: 0, - count: 0, - offset: 0, - limit: 0, - metadata: {}, - extendedMetadata: [], - themes: {}, - deletionAllowed: true, - hasEvents: false, -}; - -// Reducer for series -// @ts-expect-error TS(7006): Parameter 'action' implicitly has an 'any' type. -const series = (state = initialState, action) => { - const { type, payload } = action; - switch (type) { - case LOAD_SERIES_IN_PROGRESS: { - return { - ...state, - isLoading: true, - }; - } - case LOAD_SERIES_SUCCESS: { - const { series } = payload; - return { - ...state, - isLoading: false, - total: series.total, - count: series.count, - limit: series.limit, - offset: series.offset, - results: series.results, - }; - } - case LOAD_SERIES_FAILURE: { - return { - ...state, - isLoading: false, - }; - } - case SHOW_ACTIONS_SERIES: { - const { isShowing } = payload; - return { - ...state, - showActions: isShowing, - }; - } - case SET_SERIES_COLUMNS: { - const { updatedColumns } = payload; - return { - ...state, - columns: updatedColumns, - }; - } - case SET_SERIES_SELECTED: { - const { id } = payload; - return { - ...state, -// @ts-expect-error TS(2339): Property 'rows' does not exist on type '{ isLoadin... Remove this comment to see the full error message - rows: state.rows.map((row) => { - if (row.id === id) { - return { - ...row, - selected: !row.selected, - }; - } - return row; - }), - }; - } - case SET_SERIES_DELETION_ALLOWED: { - const { deletionAllowed, hasEvents } = payload; - return { - ...state, - deletionAllowed: deletionAllowed, - hasEvents: hasEvents, - }; - } - case LOAD_SERIES_METADATA_IN_PROGRESS: { - return { - ...state, - isLoading: true, - }; - } - case LOAD_SERIES_METADATA_SUCCESS: { - const { metadata, extendedMetadata } = payload; - return { - ...state, - isLoading: false, - metadata: metadata, - extendedMetadata: extendedMetadata, - }; - } - case LOAD_SERIES_METADATA_FAILURE: { - return { - ...state, - isLoading: false, - extendedMetadata: [], - }; - } - case LOAD_SERIES_THEMES_IN_PROGRESS: { - return { - ...state, - isLoading: true, - }; - } - case LOAD_SERIES_THEMES_SUCCESS: { - const { themes } = payload; - return { - ...state, - isLoading: false, - themes: themes, - }; - } - case LOAD_SERIES_THEMES_FAILURE: { - return { - ...state, - isLoading: false, - }; - } - default: - return state; - } -}; - -export default series; diff --git a/app/src/selectors/seriesSeletctor.ts b/app/src/selectors/seriesSeletctor.ts index cf131e0f21..91bc63ccd4 100644 --- a/app/src/selectors/seriesSeletctor.ts +++ b/app/src/selectors/seriesSeletctor.ts @@ -1,15 +1,14 @@ +import { RootState } from "../store"; + /** * This file contains selectors regarding series */ - -export const getSeries = (state: any) => state.series.results; -export const getVisibilitySeriesColumns = (state: any) => state.series.columns; -export const isShowActions = (state: any) => state.series.showActions; -export const isSeriesDeleteAllowed = (state: any) => state.series.deletionAllowed; -export const getSeriesHasEvents = (state: any) => state.series.hasEvents; -export const getSeriesMetadata = (state: any) => state.series.metadata; -export const getSeriesExtendedMetadata = (state: any) => state.series.extendedMetadata; -// @ts-expect-error TS(7006): Parameter 'state' implicitly has an 'any' type. -export const getSeriesThemes = (state) => state.series.themes; -// @ts-expect-error TS(7006): Parameter 'state' implicitly has an 'any' type. -export const getTotalSeries = (state) => state.series.total; +export const getSeries = (state: RootState) => state.series.results; +export const getVisibilitySeriesColumns = (state: RootState) => state.series.columns; +export const isShowActions = (state: RootState) => state.series.showActions; +export const isSeriesDeleteAllowed = (state: RootState) => state.series.deletionAllowed; +export const getSeriesHasEvents = (state: RootState) => state.series.hasEvents; +export const getSeriesMetadata = (state: RootState) => state.series.metadata; +export const getSeriesExtendedMetadata = (state: RootState) => state.series.extendedMetadata; +export const getSeriesThemes = (state: RootState) => state.series.themes; +export const getTotalSeries = (state: RootState) => state.series.total; diff --git a/app/src/slices/seriesSlice.ts b/app/src/slices/seriesSlice.ts new file mode 100644 index 0000000000..c328a387aa --- /dev/null +++ b/app/src/slices/seriesSlice.ts @@ -0,0 +1,373 @@ +import { PayloadAction, SerializedError, createAsyncThunk, createSlice } from '@reduxjs/toolkit' +import { seriesTableConfig } from '../configs/tableConfigs/seriesTableConfig'; +import axios from 'axios'; +import { + getURLParams, + prepareAccessPolicyRulesForPost, + prepareSeriesExtendedMetadataFieldsForPost, + prepareSeriesMetadataFieldsForPost, + transformMetadataCollection, +} from "../utils/resourceUtils"; +import { + transformToIdValueArray, + transformToObjectArray, +} from "../utils/utils"; +import { addNotification } from "../thunks/notificationThunks"; + +/** + * This file contains redux reducer for actions affecting the state of series + */ +type SeriesState = { + status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + error: SerializedError | null, + statusMetadata: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorMetadata: SerializedError | null, + statusThemes: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorThemes: SerializedError | null, + results: any[], // TODO: proper typing + columns: any, // TODO: proper typing, derive from `initialColumns` + showActions: boolean, + total: number, + count: number, + offset: number, + limit: number, + metadata: any, // TODO: proper typing + extendedMetadata: any[], // TODO: proper typing + themes: any, // TODO: proper typing + deletionAllowed: boolean, + hasEvents: boolean, +} + +// Fill columns initially with columns defined in seriesTableConfig +const initialColumns = seriesTableConfig.columns.map((column) => ({ + ...column, + deactivated: false, +})); + +// Initial state of series in redux store +const initialState: SeriesState = { + status: 'uninitialized', + error: null, + statusMetadata: 'uninitialized', + errorMetadata: null, + statusThemes: 'uninitialized', + errorThemes: null, + results: [], + columns: initialColumns, + showActions: false, + total: 0, + count: 0, + offset: 0, + limit: 0, + metadata: {}, + extendedMetadata: [], + themes: {}, + deletionAllowed: true, + hasEvents: false, +}; + +// fetch series from server +export const fetchSeries = createAsyncThunk('series/fetchSeries', 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. + // /series.json?sortorganizer={sortorganizer}&sort={sort}&filter={filter}&offset=0&limit=100 + const res = await axios.get("/admin-ng/series/series.json", { params: params }); + return res.data; +}); + +// fetch series metadata from server +export const fetchSeriesMetadata = createAsyncThunk('series/fetchSeriesMetadata', async () => { + const res = await axios.get("/admin-ng/series/new/metadata"); + const data = await res.data; + + const mainCatalog = "dublincore/series"; + let metadata = {}; + const extendedMetadata = []; + + for (const metadataCatalog of data) { + if (metadataCatalog.flavor === mainCatalog) { +// @ts-expect-error TS(2554): Expected 2 arguments, but got 1. + metadata = transformMetadataCollection({ ...metadataCatalog }); + } else { + extendedMetadata.push( +// @ts-expect-error TS(2554): Expected 2 arguments, but got 1. + transformMetadataCollection({ ...metadataCatalog }) + ); + } + } + + return { metadata, extendedMetadata } +}); + +// fetch series themes from server +export const fetchSeriesThemes = createAsyncThunk('series/fetchSeriesThemes', async () => { + let res = await axios.get("/admin-ng/series/new/themes"); + const data = await res.data; + const themes = transformToObjectArray(data); + return themes; +}); + +// post new series to backend +export const postNewSeries = createAsyncThunk('series/postNewSeries', async (params: {values: any, metadataInfo: any, extendedMetadata: any}, {dispatch}) => { + const { values, metadataInfo, extendedMetadata } = params + + let metadataFields, extendedMetadataFields, metadata, access; + + // prepare metadata provided by user + metadataFields = prepareSeriesMetadataFieldsForPost( + metadataInfo.fields, + values + ); + extendedMetadataFields = prepareSeriesExtendedMetadataFieldsForPost( + extendedMetadata, + values + ); + + // metadata for post request + metadata = [ + { + flavor: metadataInfo.flavor, + title: metadataInfo.title, + fields: metadataFields, + }, + ]; + + for (const entry of extendedMetadataFields) { + metadata.push(entry); + } + + access = prepareAccessPolicyRulesForPost(values.acls); + + let jsonData = { + metadata: metadata, + options: {}, + access: access, + }; + + if (values.theme !== "") { + jsonData = { + ...jsonData, +// @ts-expect-error TS(2322): Type '{ theme: number; metadata: { flavor: any; ti... Remove this comment to see the full error message + theme: parseInt(values.theme), + }; + } + + let data = new URLSearchParams(); + data.append("metadata", JSON.stringify(jsonData)); + + // Todo: process bar notification + axios + .post("/admin-ng/series/new", data.toString(), { + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + }) + .then((response) => { + console.info(response); + dispatch(addNotification("success", "SERIES_ADDED")); + }) + .catch((response) => { + console.error(response); + dispatch(addNotification("error", "SERIES_NOT_SAVED")); + }); +}); + +// check for events of the series and if deleting the series if it has events is allowed +export const checkForEventsDeleteSeriesModal = createAsyncThunk('series/checkForEventsDeleteSeriesModal', async (id: any, {dispatch}) => { + const hasEventsRequest = await axios.get( + `/admin-ng/series/${id}/hasEvents.json` + ); + const hasEventsResponse = await hasEventsRequest.data; + const hasEvents = hasEventsResponse.hasEvents; + + const deleteWithEventsAllowedRequest = await axios.get( + "/admin-ng/series/configuration.json" + ); + const deleteWithEventsAllowedResponse = await deleteWithEventsAllowedRequest.data; + const deleteWithEventsAllowed = + deleteWithEventsAllowedResponse.deleteSeriesWithEventsAllowed; + + dispatch( + setSeriesDeletionAllowed({ deletionAllowed: !hasEvents || deleteWithEventsAllowed, hasEvents: hasEvents }) + ); +}); + +// delete series with provided id +export const deleteSeries = createAsyncThunk('series/deleteSeries', async (id: any, {dispatch}) => { + // API call for deleting a series + axios + .delete(`/admin-ng/series/${id}`) + .then((res) => { + console.info(res); + // add success notification + dispatch(addNotification("success", "SERIES_DELETED")); + }) + .catch((res) => { + console.error(res); + // add error notification + dispatch(addNotification("error", "SERIES_NOT_DELETED")); + }); +}); + +// delete series with provided ids +export const deleteMultipleSeries = createAsyncThunk('series/deleteMultipleSeries', async (series: any, {dispatch}) => { + let data = []; + + for (let i = 0; i < series.length; i++) { + if (series[i].selected) { + data.push(series[i].id); + } + } + + axios + .post("/admin-ng/series/deleteSeries", data) + .then((res) => { + console.info(res); + //add success notification + dispatch(addNotification("success", "SERIES_DELETED")); + }) + .catch((res) => { + console.error(res); + //add error notification + dispatch(addNotification("error", "SERIES_NOT_DELETED")); + }); +}); + +// Get names and ids of selectable series +export const fetchSeriesOptions = async () => { + let data = await axios.get("/admin-ng/resources/SERIES.json"); + + const response = await data.data; + + const seriesCollection = []; + for (const series of transformToIdValueArray(response)) { + seriesCollection.push({ value: series.id, name: series.value }); + } + + return seriesCollection; +}; + +// Check if a series has events +// @ts-expect-error TS(7006): Parameter 'seriesId' implicitly has an 'any' type. +export const hasEvents = async (seriesId) => { + let data = await axios.get(`/admin-ng/series/${seriesId}/hasEvents.json`); + + return (await data.data).hasEvents; +}; + +// Get series configuration and flag indicating if series with events is allowed to delete +export const getSeriesConfig = async () => { + let data = await axios.get("/admin-ng/series/configuration.json"); + + const response = await data.data; + + return !!response.deleteSeriesWithEventsAllowed; +}; + +const seriesSlice = createSlice({ + name: 'series', + initialState, + reducers: { + setSeriesColumns(state, action: PayloadAction<{ + updatedColumns: SeriesState["columns"], + }>) { + state.columns = action.payload.updatedColumns; + }, + showActionsSeries(state, action: PayloadAction< + SeriesState["showActions"] + >) { + state.showActions = action.payload; + }, + setSeriesSelected(state, action: PayloadAction< + any + >) { + const id = action.payload + // state.: state.rows.map((row) => { + // if (row.id === id) { + // return { + // ...row, + // selected: !row.selected, + // }; + // } + // return row; + // }), + }, + setSeriesDeletionAllowed(state, action: PayloadAction<{ + deletionAllowed: SeriesState["deletionAllowed"], + hasEvents: SeriesState["hasEvents"], + }>) { + state.deletionAllowed = action.payload.deletionAllowed; + state.hasEvents = action.payload.hasEvents; + }, + }, + // These are used for thunks + extraReducers: builder => { + builder + .addCase(fetchSeries.pending, (state) => { + state.status = 'loading'; + }) + .addCase(fetchSeries.fulfilled, (state, action: PayloadAction<{ + total: SeriesState["total"], + count: SeriesState["count"], + limit: SeriesState["limit"], + offset: SeriesState["offset"], + results: SeriesState["results"], + }>) => { + state.status = 'succeeded'; + const series = action.payload; + state.total = series.total; + state.count = series.count; + state.limit = series.limit; + state.offset = series.offset; + state.results = series.results; + }) + .addCase(fetchSeries.rejected, (state, action) => { + state.status = 'failed'; + state.error = action.error; + }) + .addCase(fetchSeriesMetadata.pending, (state) => { + state.statusMetadata = 'loading'; + }) + .addCase(fetchSeriesMetadata.fulfilled, (state, action: PayloadAction<{ + metadata: SeriesState["metadata"], + extendedMetadata: SeriesState["extendedMetadata"], + }>) => { + state.statusMetadata = 'succeeded'; + const seriesMetadata = action.payload; + state.metadata = seriesMetadata.metadata; + state.extendedMetadata = seriesMetadata.extendedMetadata; + }) + .addCase(fetchSeriesMetadata.rejected, (state, action) => { + state.statusMetadata = 'failed'; + state.extendedMetadata = []; + state.errorMetadata = action.error; + }) + .addCase(fetchSeriesThemes.pending, (state) => { + state.statusThemes = 'loading'; + }) + .addCase(fetchSeriesThemes.fulfilled, (state, action: PayloadAction< + SeriesState["themes"] + >) => { + state.statusThemes = 'succeeded'; + const seriesThemes = action.payload; + state.themes = seriesThemes; + }) + .addCase(fetchSeriesThemes.rejected, (state, action) => { + state.statusThemes = 'failed'; + state.errorThemes = action.error; + }); + } +}); + +export const { + setSeriesColumns, + showActionsSeries, + setSeriesSelected, + setSeriesDeletionAllowed, +} = seriesSlice.actions; + +// Export the slice reducer as the default export +export default seriesSlice.reducer; diff --git a/app/src/store.ts b/app/src/store.ts index 790807712c..4cbc31faaf 100644 --- a/app/src/store.ts +++ b/app/src/store.ts @@ -7,7 +7,7 @@ import tableFilters from "./reducers/tableFilterReducers"; import tableFilterProfiles from "./reducers/tableFilterProfilesReducer"; import events from "./reducers/eventReducers"; import table from "./reducers/tableReducers"; -import series from "./reducers/seriesReducer"; +import series from "./slices/seriesSlice"; import recordings from "./reducers/recordingReducer"; import jobs from "./reducers/jobReducer"; import servers from "./reducers/serverReducer"; diff --git a/app/src/thunks/eventThunks.ts b/app/src/thunks/eventThunks.ts index 23b853d392..c3f4f3844e 100644 --- a/app/src/thunks/eventThunks.ts +++ b/app/src/thunks/eventThunks.ts @@ -30,7 +30,7 @@ import { getAssetUploadOptions, getSchedulingEditedEvents, } from "../selectors/eventSelectors"; -import { fetchSeriesOptions } from "./seriesThunks"; +import { fetchSeriesOptions } from "../slices/seriesSlice"; import { removeNotification } from "../actions/notificationActions"; // fetch events from server diff --git a/app/src/thunks/seriesThunks.ts b/app/src/thunks/seriesThunks.ts deleted file mode 100644 index 304c215767..0000000000 --- a/app/src/thunks/seriesThunks.ts +++ /dev/null @@ -1,259 +0,0 @@ -import axios from "axios"; -import { - loadSeriesFailure, - loadSeriesInProgress, - loadSeriesMetadataInProgress, - loadSeriesMetadataSuccess, - loadSeriesSuccess, - loadSeriesThemesFailure, - loadSeriesThemesInProgress, - loadSeriesThemesSuccess, - setSeriesDeletionAllowed, -} from "../actions/seriesActions"; -import { - getURLParams, - prepareAccessPolicyRulesForPost, - prepareSeriesExtendedMetadataFieldsForPost, - prepareSeriesMetadataFieldsForPost, - transformMetadataCollection, -} from "../utils/resourceUtils"; -import { - transformToIdValueArray, - transformToObjectArray, -} from "../utils/utils"; -import { addNotification } from "./notificationThunks"; - -// fetch series from server -// @ts-expect-error TS(7006): Parameter 'dispatch' implicitly has an 'any' type. -export const fetchSeries = () => async (dispatch, getState) => { - try { - dispatch(loadSeriesInProgress()); - - const state = getState(); - let params = getURLParams(state); - - // /series.json?sortorganizer={sortorganizer}&sort={sort}&filter={filter}&offset=0&limit=100 - let data = await axios.get("/admin-ng/series/series.json", { - params: params, - }); - - const series = await data.data; - dispatch(loadSeriesSuccess(series)); - } catch (e) { - dispatch(loadSeriesFailure()); - console.error(e); - } -}; - -// fetch series metadata from server -// @ts-expect-error TS(7006): Parameter 'dispatch' implicitly has an 'any' type. -export const fetchSeriesMetadata = () => async (dispatch) => { - try { - dispatch(loadSeriesMetadataInProgress()); - - let data = await axios.get("/admin-ng/series/new/metadata"); - const response = await data.data; - - const mainCatalog = "dublincore/series"; - let metadata = {}; - const extendedMetadata = []; - - for (const metadataCatalog of response) { - if (metadataCatalog.flavor === mainCatalog) { -// @ts-expect-error TS(2554): Expected 2 arguments, but got 1. - metadata = transformMetadataCollection({ ...metadataCatalog }); - } else { - extendedMetadata.push( -// @ts-expect-error TS(2554): Expected 2 arguments, but got 1. - transformMetadataCollection({ ...metadataCatalog }) - ); - } - } - - dispatch(loadSeriesMetadataSuccess(metadata, extendedMetadata)); - } catch (e) { - dispatch(loadSeriesFailure()); - console.error(e); - } -}; - -// fetch series themes from server -// @ts-expect-error TS(7006): Parameter 'dispatch' implicitly has an 'any' type. -export const fetchSeriesThemes = () => async (dispatch) => { - try { - dispatch(loadSeriesThemesInProgress()); - - let data = await axios.get("/admin-ng/series/new/themes"); - - const response = await data.data; - - const themes = transformToObjectArray(response); - - dispatch(loadSeriesThemesSuccess(themes)); - } catch (e) { - dispatch(loadSeriesThemesFailure()); - console.error(e); - } -}; - -// post new series to backend -// @ts-expect-error TS(7006): Parameter 'values' implicitly has an 'any' type. -export const postNewSeries = (values, metadataInfo, extendedMetadata) => async ( -// @ts-expect-error TS(7006): Parameter 'dispatch' implicitly has an 'any' type. - dispatch -) => { - let metadataFields, extendedMetadataFields, metadata, access; - - // prepare metadata provided by user - metadataFields = prepareSeriesMetadataFieldsForPost( - metadataInfo.fields, - values - ); - extendedMetadataFields = prepareSeriesExtendedMetadataFieldsForPost( - extendedMetadata, - values - ); - - // metadata for post request - metadata = [ - { - flavor: metadataInfo.flavor, - title: metadataInfo.title, - fields: metadataFields, - }, - ]; - - for (const entry of extendedMetadataFields) { - metadata.push(entry); - } - - access = prepareAccessPolicyRulesForPost(values.acls); - - let jsonData = { - metadata: metadata, - options: {}, - access: access, - }; - - if (values.theme !== "") { - jsonData = { - ...jsonData, -// @ts-expect-error TS(2322): Type '{ theme: number; metadata: { flavor: any; ti... Remove this comment to see the full error message - theme: parseInt(values.theme), - }; - } - - let data = new URLSearchParams(); - data.append("metadata", JSON.stringify(jsonData)); - - // Todo: process bar notification - axios - .post("/admin-ng/series/new", data.toString(), { - headers: { - "Content-Type": "application/x-www-form-urlencoded", - }, - }) - .then((response) => { - console.info(response); - dispatch(addNotification("success", "SERIES_ADDED")); - }) - .catch((response) => { - console.error(response); - dispatch(addNotification("error", "SERIES_NOT_SAVED")); - }); -}; - -// check for events of the series and if deleting the series if it has events is allowed -// @ts-expect-error TS(7006): Parameter 'id' implicitly has an 'any' type. -export const checkForEventsDeleteSeriesModal = (id) => async (dispatch) => { - const hasEventsRequest = await axios.get( - `/admin-ng/series/${id}/hasEvents.json` - ); - const hasEventsResponse = await hasEventsRequest.data; - const hasEvents = hasEventsResponse.hasEvents; - - const deleteWithEventsAllowedRequest = await axios.get( - "/admin-ng/series/configuration.json" - ); - const deleteWithEventsAllowedResponse = await deleteWithEventsAllowedRequest.data; - const deleteWithEventsAllowed = - deleteWithEventsAllowedResponse.deleteSeriesWithEventsAllowed; - - dispatch( - setSeriesDeletionAllowed(!hasEvents || deleteWithEventsAllowed, hasEvents) - ); -}; - -// delete series with provided id -// @ts-expect-error TS(7006): Parameter 'id' implicitly has an 'any' type. -export const deleteSeries = (id) => async (dispatch) => { - // API call for deleting a series - axios - .delete(`/admin-ng/series/${id}`) - .then((res) => { - console.info(res); - // add success notification - dispatch(addNotification("success", "SERIES_DELETED")); - }) - .catch((res) => { - console.error(res); - // add error notification - dispatch(addNotification("error", "SERIES_NOT_DELETED")); - }); -}; - -// delete series with provided ids -// @ts-expect-error TS(7006): Parameter 'series' implicitly has an 'any' type. -export const deleteMultipleSeries = (series) => async (dispatch) => { - let data = []; - - for (let i = 0; i < series.length; i++) { - if (series[i].selected) { - data.push(series[i].id); - } - } - - axios - .post("/admin-ng/series/deleteSeries", data) - .then((res) => { - console.info(res); - //add success notification - dispatch(addNotification("success", "SERIES_DELETED")); - }) - .catch((res) => { - console.error(res); - //add error notification - dispatch(addNotification("error", "SERIES_NOT_DELETED")); - }); -}; - -// Get names and ids of selectable series -export const fetchSeriesOptions = async () => { - let data = await axios.get("/admin-ng/resources/SERIES.json"); - - const response = await data.data; - - const seriesCollection = []; - for (const series of transformToIdValueArray(response)) { - seriesCollection.push({ value: series.id, name: series.value }); - } - - return seriesCollection; -}; - -// Check if a series has events -// @ts-expect-error TS(7006): Parameter 'seriesId' implicitly has an 'any' type. -export const hasEvents = async (seriesId) => { - let data = await axios.get(`/admin-ng/series/${seriesId}/hasEvents.json`); - - return (await data.data).hasEvents; -}; - -// Get series configuration and flag indicating if series with events is allowed to delete -export const getSeriesConfig = async () => { - let data = await axios.get("/admin-ng/series/configuration.json"); - - const response = await data.data; - - return !!response.deleteSeriesWithEventsAllowed; -}; diff --git a/app/src/thunks/serviceThunks.ts b/app/src/thunks/serviceThunks.ts index 2a3ebae7a1..5716bdb880 100644 --- a/app/src/thunks/serviceThunks.ts +++ b/app/src/thunks/serviceThunks.ts @@ -1,16 +1,16 @@ import axios from "axios"; import { loadServicesFailure, + loadServicesInProgress, loadServicesSuccess, } from "../actions/serviceActions"; -import { loadSeriesInProgress } from "../actions/seriesActions"; import { getURLParams } from "../utils/resourceUtils"; // fetch services from server // @ts-expect-error TS(7006): Parameter 'dispatch' implicitly has an 'any' type. export const fetchServices = () => async (dispatch, getState) => { try { - dispatch(loadSeriesInProgress()); + dispatch(loadServicesInProgress()); const state = getState(); let params = getURLParams(state); diff --git a/app/src/thunks/tableThunks.ts b/app/src/thunks/tableThunks.ts index 3220b05dde..ac2e23616c 100644 --- a/app/src/thunks/tableThunks.ts +++ b/app/src/thunks/tableThunks.ts @@ -21,10 +21,6 @@ import { setEventColumns, showActions as showEventsActions, } from "../actions/eventActions"; -import { - setSeriesColumns, - showActions as showSeriesActions, -} from "../actions/seriesActions"; import { getPageOffset, getResourceType, @@ -33,7 +29,11 @@ import { getTablePagination, } from "../selectors/tableSelectors"; import { fetchEvents } from "./eventThunks"; -import { fetchSeries } from "./seriesThunks"; +import { + fetchSeries, + setSeriesColumns, + showActionsSeries, +} from "../slices/seriesSlice"; import { fetchRecordings } from "./recordingThunks"; import { fetchJobs } from "./jobThunks"; import { fetchServers } from "./serverThunks"; @@ -559,7 +559,7 @@ export const changeAllSelected = (selected) => (dispatch, getState) => { break; } case "series": { - dispatch(showSeriesActions(true)); + dispatch(showActionsSeries(true)); break; } } @@ -572,7 +572,7 @@ export const changeAllSelected = (selected) => (dispatch, getState) => { break; } case "series": { - dispatch(showSeriesActions(false)); + dispatch(showActionsSeries(false)); break; } } @@ -609,9 +609,9 @@ export const changeColumnSelection = (updatedColumns) => async ( await dispatch(setSeriesColumns(updatedColumns)); if (getSelectedRows(state).length > 0) { - dispatch(showSeriesActions(true)); + dispatch(showActionsSeries(true)); } else { - dispatch(showSeriesActions(false)); + dispatch(showActionsSeries(false)); } dispatch(loadSeriesIntoTable()); @@ -679,9 +679,9 @@ export const changeRowSelection = (id, selected) => (dispatch, getState) => { } case "series": { if (getSelectedRows(state).length > 0) { - dispatch(showSeriesActions(true)); + dispatch(showActionsSeries(true)); } else { - dispatch(showSeriesActions(false)); + dispatch(showActionsSeries(false)); } break; } From 31d4e9418d4a28ba9196210122701146f1c82ab6 Mon Sep 17 00:00:00 2001 From: Arnei Date: Mon, 12 Feb 2024 16:09:32 +0100 Subject: [PATCH 4/8] Add typing for series result Adds typing on what we expect the backend to return for series. --- app/src/slices/seriesSlice.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/app/src/slices/seriesSlice.ts b/app/src/slices/seriesSlice.ts index c328a387aa..d75f437e58 100644 --- a/app/src/slices/seriesSlice.ts +++ b/app/src/slices/seriesSlice.ts @@ -17,6 +17,19 @@ import { addNotification } from "../thunks/notificationThunks"; /** * This file contains redux reducer for actions affecting the state of series */ +type Series = { + contributors: string[], + createdBy?: string, + creation_date?: string, + id: string, + language?: string, + license?: string, + managedAcl?: string, + organizers: string[], + rightsHolder?: string, + title: string, +} + type SeriesState = { status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', error: SerializedError | null, @@ -24,7 +37,7 @@ type SeriesState = { errorMetadata: SerializedError | null, statusThemes: 'uninitialized' | 'loading' | 'succeeded' | 'failed', errorThemes: SerializedError | null, - results: any[], // TODO: proper typing + results: Series[], columns: any, // TODO: proper typing, derive from `initialColumns` showActions: boolean, total: number, From d3e7c95281a60489315b19400075941b6f74de80 Mon Sep 17 00:00:00 2001 From: Arnei Date: Mon, 12 Feb 2024 17:00:17 +0100 Subject: [PATCH 5/8] Add typings for series metadata and themes Adds more typings to the series state. --- .../ModalTabsAndPages/NewThemePage.tsx | 23 +++++------- .../partials/wizards/NewSeriesSummary.tsx | 4 +- app/src/slices/seriesSlice.ts | 37 ++++++++++++++++--- 3 files changed, 41 insertions(+), 23 deletions(-) diff --git a/app/src/components/events/partials/ModalTabsAndPages/NewThemePage.tsx b/app/src/components/events/partials/ModalTabsAndPages/NewThemePage.tsx index dc754842ed..2bb0196105 100644 --- a/app/src/components/events/partials/ModalTabsAndPages/NewThemePage.tsx +++ b/app/src/components/events/partials/ModalTabsAndPages/NewThemePage.tsx @@ -17,12 +17,16 @@ const NewThemePage = ({ const seriesThemes = useAppSelector(state => getSeriesThemes(state)); -// @ts-expect-error TS(7006): Parameter 'id' implicitly has an 'any' type. - const getDescription = (id) => { -// @ts-expect-error TS(7006): Parameter 'theme' implicitly has an 'any' type. + const getDescription = (id: string) => { const theme = seriesThemes.find((theme) => theme.id === id); - return theme.description; + return theme?.description; + }; + + const getName = (id: string) => { + const theme = seriesThemes.find((theme) => theme.id === id); + + return theme?.name; }; return ( @@ -45,16 +49,7 @@ const NewThemePage = ({ formik.values.theme === theme.id - ) - ? seriesThemes.find( -// @ts-expect-error TS(7006): Parameter 'theme' implicitly has an 'any' type. - (theme) => - formik.values.theme === theme.id - ).name - : "" + getName(formik.values.theme) ? getName(formik.values.theme) : "" } options={seriesThemes} type={"newTheme"} diff --git a/app/src/components/events/partials/wizards/NewSeriesSummary.tsx b/app/src/components/events/partials/wizards/NewSeriesSummary.tsx index 6aaa899f7a..73495388c7 100644 --- a/app/src/components/events/partials/wizards/NewSeriesSummary.tsx +++ b/app/src/components/events/partials/wizards/NewSeriesSummary.tsx @@ -1,6 +1,5 @@ import React from "react"; import { useTranslation } from "react-i18next"; -import { connect } from "react-redux"; import { getSeriesExtendedMetadata, getSeriesMetadata, @@ -30,7 +29,6 @@ const NewSeriesSummary = ({ const seriesThemes = useAppSelector(state => getSeriesThemes(state)); // Get additional information about chosen series theme -// @ts-expect-error TS(7006): Parameter 'theme' implicitly has an 'any' type. const theme = seriesThemes.find((theme) => theme.id === formik.values.theme); return ( @@ -71,7 +69,7 @@ const NewSeriesSummary = ({ {t("EVENTS.SERIES.NEW.THEME.CAPTION")} - {theme.name} + {theme?.name} diff --git a/app/src/slices/seriesSlice.ts b/app/src/slices/seriesSlice.ts index d75f437e58..a1420348ba 100644 --- a/app/src/slices/seriesSlice.ts +++ b/app/src/slices/seriesSlice.ts @@ -30,6 +30,27 @@ type Series = { title: string, } +type MetadataCatalog = { + title: string, + flavor: string, + fields: { + collection?: {}[], // different for e.g. languages and presenters + id: string, + label: string, + readOnly: boolean, + required: boolean, + translatable?: boolean, + type: string, + value: string | string[], + }[] +} + +type Theme = { + description: string, + id: string, + name: string, +} + type SeriesState = { status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', error: SerializedError | null, @@ -44,9 +65,9 @@ type SeriesState = { count: number, offset: number, limit: number, - metadata: any, // TODO: proper typing - extendedMetadata: any[], // TODO: proper typing - themes: any, // TODO: proper typing + metadata: MetadataCatalog, + extendedMetadata: MetadataCatalog[], + themes: Theme[], deletionAllowed: boolean, hasEvents: boolean, } @@ -72,9 +93,13 @@ const initialState: SeriesState = { count: 0, offset: 0, limit: 0, - metadata: {}, + metadata: { + title: "", + flavor: "", + fields: [], + }, extendedMetadata: [], - themes: {}, + themes: [], deletionAllowed: true, hasEvents: false, }; @@ -97,7 +122,7 @@ export const fetchSeriesMetadata = createAsyncThunk('series/fetchSeriesMetadata' const data = await res.data; const mainCatalog = "dublincore/series"; - let metadata = {}; + let metadata: any = {}; const extendedMetadata = []; for (const metadataCatalog of data) { From 81efc815f9baaa2bab29ea39cbdefdf9ca8e5c2a Mon Sep 17 00:00:00 2001 From: Arnei Date: Mon, 12 Feb 2024 17:09:14 +0100 Subject: [PATCH 6/8] Remove unused setter in series state Removes and unused hooks that should have been with the table functions anyway. --- app/src/slices/seriesSlice.ts | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/app/src/slices/seriesSlice.ts b/app/src/slices/seriesSlice.ts index a1420348ba..e59620de77 100644 --- a/app/src/slices/seriesSlice.ts +++ b/app/src/slices/seriesSlice.ts @@ -319,20 +319,6 @@ const seriesSlice = createSlice({ >) { state.showActions = action.payload; }, - setSeriesSelected(state, action: PayloadAction< - any - >) { - const id = action.payload - // state.: state.rows.map((row) => { - // if (row.id === id) { - // return { - // ...row, - // selected: !row.selected, - // }; - // } - // return row; - // }), - }, setSeriesDeletionAllowed(state, action: PayloadAction<{ deletionAllowed: SeriesState["deletionAllowed"], hasEvents: SeriesState["hasEvents"], @@ -403,7 +389,6 @@ const seriesSlice = createSlice({ export const { setSeriesColumns, showActionsSeries, - setSeriesSelected, setSeriesDeletionAllowed, } = seriesSlice.actions; From a176ae29ffbe2681bedafe29d002011e0239408a Mon Sep 17 00:00:00 2001 From: Arnei Date: Wed, 14 Feb 2024 16:46:54 +0100 Subject: [PATCH 7/8] Fix series table crashing when changing columns --- app/src/slices/seriesSlice.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/slices/seriesSlice.ts b/app/src/slices/seriesSlice.ts index e59620de77..6420d05ee3 100644 --- a/app/src/slices/seriesSlice.ts +++ b/app/src/slices/seriesSlice.ts @@ -309,10 +309,10 @@ const seriesSlice = createSlice({ name: 'series', initialState, reducers: { - setSeriesColumns(state, action: PayloadAction<{ - updatedColumns: SeriesState["columns"], - }>) { - state.columns = action.payload.updatedColumns; + setSeriesColumns(state, action: PayloadAction< + SeriesState["columns"] + >) { + state.columns = action.payload; }, showActionsSeries(state, action: PayloadAction< SeriesState["showActions"] From 2e1b3d6964ca98303ea4c8f4404108ae978a9b9d Mon Sep 17 00:00:00 2001 From: Arnei Date: Wed, 14 Feb 2024 16:49:58 +0100 Subject: [PATCH 8/8] Fix table filters in series table --- app/src/components/events/Series.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/src/components/events/Series.tsx b/app/src/components/events/Series.tsx index 7c35b9478e..c65842c442 100644 --- a/app/src/components/events/Series.tsx +++ b/app/src/components/events/Series.tsx @@ -73,6 +73,11 @@ const Series = ({ const series = useAppSelector(state => getTotalSeries(state)); const showActions = useAppSelector(state => isShowActions(state)); + // TODO: Get rid of the wrappers when modernizing redux is done + const fetchSeriesWrapper = () => { + dispatch(fetchSeries()) + } + const loadEvents = () => { // Reset the current page to first page resetOffset(); @@ -247,7 +252,7 @@ const Series = ({ {/* Include filters component */}