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 0dc312d465..33a94e3215 100644 --- a/app/src/components/events/Events.tsx +++ b/app/src/components/events/Events.tsx @@ -18,7 +18,6 @@ import { loadEventsIntoTable, loadSeriesIntoTable, } from "../../thunks/tableThunks"; -import { fetchSeries } from "../../thunks/seriesThunks"; import { fetchFilters, fetchStats } from "../../thunks/tableFilterThunks"; import { getTotalEvents, @@ -42,6 +41,7 @@ import { fetchEvents, setShowActions, } from "../../slices/eventSlice"; +import { fetchSeries } from "../../slices/seriesSlice"; // References for detecting a click outside of the container of the dropdown menu const containerAction = React.createRef(); @@ -52,8 +52,6 @@ const containerAction = React.createRef(); const Events = ({ // @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 'loadingFilters' implicitly has an... Remove this comment to see the full error message @@ -111,7 +109,7 @@ const Events = ({ resetOffset(); //fetching series from server - loadingSeries(); + dispatch(fetchSeries()); //load series into table loadingSeriesIntoTable(); @@ -352,7 +350,6 @@ const mapStateToProps = (state) => ({ // @ts-expect-error TS(7006): Parameter 'dispatch' implicitly has an 'any' type. const mapDispatchToProps = (dispatch) => ({ 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 34ae1ff71f..16b8c62cee 100644 --- a/app/src/components/events/Series.tsx +++ b/app/src/components/events/Series.tsx @@ -9,12 +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 { - fetchSeries, - fetchSeriesMetadata, - fetchSeriesThemes, -} from "../../thunks/seriesThunks"; +import { seriesTemplateMap } from "../../configs/tableConfigs/seriesTableMap"; import { loadEventsIntoTable, loadSeriesIntoTable, @@ -28,12 +23,17 @@ 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 } from "../../store"; +import { useAppDispatch, useAppSelector } from "../../store"; import { fetchEvents } from "../../slices/eventSlice"; +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(); @@ -42,32 +42,20 @@ 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 '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, }) => { @@ -80,6 +68,14 @@ const Series = ({ let location = useLocation(); + 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(); @@ -96,7 +92,7 @@ const Series = ({ const loadSeries = async () => { //fetching series from server - await loadingSeries(); + await dispatch(fetchSeries()); //load series into table loadingSeriesIntoTable(); @@ -110,7 +106,7 @@ const Series = ({ resetTextFilter(); // disable actions button - setShowActions(false); + dispatch(showActionsSeries(false)); // Load events on mount loadSeries().then((r) => console.info(r)); @@ -151,8 +147,8 @@ const Series = ({ }; const showNewSeriesModal = async () => { - await loadingSeriesMetadata(); - await loadingSeriesThemes(); + await dispatch(fetchSeriesMetadata()); + await dispatch(fetchSeriesThemes()); setNewSeriesModal(true); }; @@ -254,7 +250,7 @@ const Series = ({ {/* Include filters component */} @@ -273,8 +269,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,18 +276,13 @@ 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()), 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..2bb0196105 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,16 +12,21 @@ const NewThemePage = ({ formik, nextPage, previousPage, - seriesThemes }: any) => { const { t } = useTranslation(); -// @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 seriesThemes = useAppSelector(state => getSeriesThemes(state)); + + const getDescription = (id: string) => { + const theme = seriesThemes.find((theme) => theme.id === id); + + return theme?.description; + }; + + const getName = (id: string) => { const theme = seriesThemes.find((theme) => theme.id === id); - return theme.description; + return theme?.name; }; return ( @@ -44,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"} @@ -92,9 +88,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..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, @@ -10,6 +9,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,17 +21,14 @@ 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); return ( @@ -72,7 +69,7 @@ const NewSeriesSummary = ({ {t("EVENTS.SERIES.NEW.THEME.CAPTION")} - {theme.name} + {theme?.name} @@ -92,12 +89,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 da8d097df5..a40c74c1ec 100644 --- a/app/src/components/shared/MainNav.tsx +++ b/app/src/components/shared/MainNav.tsx @@ -22,7 +22,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"; @@ -30,6 +29,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"; import { fetchJobs } from "../../slices/jobSlice"; /** @@ -42,8 +42,6 @@ const MainNav = ({ toggleMenu, // @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 @@ -97,7 +95,7 @@ const MainNav = ({ loadingStats(); // Fetching events from server - // dispatch(fetchEvents()); + dispatch(fetchEvents()); // Load events into table loadingEventsIntoTable(); @@ -110,7 +108,7 @@ const MainNav = ({ resetOffset(); // Fetching series from server - loadingSeries(); + dispatch(fetchSeries()); // Load series into table loadingSeriesIntoTable(); @@ -340,7 +338,6 @@ const mapStateToProps = (state) => ({ // @ts-expect-error TS(7006): Parameter 'dispatch' implicitly has an 'any' type. const mapDispatchToProps = (dispatch) => ({ 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/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, +}; 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/eventSlice.ts b/app/src/slices/eventSlice.ts index df09bee989..ac81461e14 100644 --- a/app/src/slices/eventSlice.ts +++ b/app/src/slices/eventSlice.ts @@ -16,10 +16,10 @@ import { weekdays, WORKFLOW_UPLOAD_ASSETS_NON_TRACK, } from "../configs/modalConfig"; -import { addNotification } from '../thunks/notificationThunks'; +import { addNotification, addNotificationWithId } from "../thunks/notificationThunks"; import { removeNotification } from '../actions/notificationActions'; import { getAssetUploadOptions, getSchedulingEditedEvents } from '../selectors/eventSelectors'; -import { fetchSeriesOptions } from '../thunks/seriesThunks'; +import { fetchSeriesOptions } from "../slices/seriesSlice"; import { RootState } from '../store'; import { fetchAssetUploadOptions } from '../thunks/assetsThunks'; diff --git a/app/src/slices/seriesSlice.ts b/app/src/slices/seriesSlice.ts new file mode 100644 index 0000000000..6420d05ee3 --- /dev/null +++ b/app/src/slices/seriesSlice.ts @@ -0,0 +1,396 @@ +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 Series = { + contributors: string[], + createdBy?: string, + creation_date?: string, + id: string, + language?: string, + license?: string, + managedAcl?: string, + organizers: string[], + rightsHolder?: string, + 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, + statusMetadata: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorMetadata: SerializedError | null, + statusThemes: 'uninitialized' | 'loading' | 'succeeded' | 'failed', + errorThemes: SerializedError | null, + results: Series[], + columns: any, // TODO: proper typing, derive from `initialColumns` + showActions: boolean, + total: number, + count: number, + offset: number, + limit: number, + metadata: MetadataCatalog, + extendedMetadata: MetadataCatalog[], + themes: Theme[], + 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: { + title: "", + flavor: "", + fields: [], + }, + 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: any = {}; + 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< + SeriesState["columns"] + >) { + state.columns = action.payload; + }, + showActionsSeries(state, action: PayloadAction< + SeriesState["showActions"] + >) { + state.showActions = action.payload; + }, + 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, + 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 467ea950aa..55e085fb59 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 "./slices/eventSlice"; import table from "./reducers/tableReducers"; -import series from "./reducers/seriesReducer"; +import series from "./slices/seriesSlice"; import recordings from "./reducers/recordingReducer"; import jobs from "./slices/jobSlice"; import servers from "./reducers/serverReducer"; 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 a629dd7753..3e0980dcca 100644 --- a/app/src/thunks/tableThunks.ts +++ b/app/src/thunks/tableThunks.ts @@ -22,10 +22,6 @@ import { setShowActions as showEventsActions, fetchEvents, } from "../slices/eventSlice"; -import { - setSeriesColumns, - showActions as showSeriesActions, -} from "../actions/seriesActions"; import { getPageOffset, getResourceType, @@ -33,7 +29,11 @@ import { getTablePages, getTablePagination, } from "../selectors/tableSelectors"; -import { fetchSeries } from "./seriesThunks"; +import { + fetchSeries, + setSeriesColumns, + showActionsSeries, +} from "../slices/seriesSlice"; import { fetchRecordings } from "./recordingThunks"; import { fetchJobs, setJobColumns } from "../slices/jobSlice"; import { fetchServers } from "./serverThunks"; @@ -555,7 +555,7 @@ export const changeAllSelected = (selected) => (dispatch, getState) => { break; } case "series": { - dispatch(showSeriesActions(true)); + dispatch(showActionsSeries(true)); break; } } @@ -568,7 +568,7 @@ export const changeAllSelected = (selected) => (dispatch, getState) => { break; } case "series": { - dispatch(showSeriesActions(false)); + dispatch(showActionsSeries(false)); break; } } @@ -605,9 +605,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()); @@ -675,9 +675,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; }