From 1a5c0db2749b92e654e4731e76b057e9d3573205 Mon Sep 17 00:00:00 2001 From: Arnei Date: Wed, 17 Jun 2026 16:12:38 +0200 Subject: [PATCH 1/2] Remove or comment on eslint exceptions Eslint exceptions in general should be avoided. If absolutely necessary, they should at least have an explaining comment accompanying them. This patch aims at achieving that. Arguably some of the eslint exceptions I left in could still be removed, but that would usually mean major programming effort. Of note is that this patch includes `dispatch` in useEffect dependency arrays, even if that useEffect hook should run only once on mount. We can do that because dispatch is stable, it's just that the lint rules can't know that. See also official redux docs: https://react-redux.js.org/api/hooks --- src/App.tsx | 4 +-- src/components/About.tsx | 1 + src/components/Header.tsx | 11 ++++---- src/components/events/Events.tsx | 3 +- src/components/events/Series.tsx | 3 +- .../ModalTabsAndPages/DetailsTobiraTab.tsx | 3 +- .../EditScheduledEventsEditPage.tsx | 2 ++ .../EventDetailsAssetsTab.tsx | 2 ++ .../EventDetailsCommentsTab.tsx | 2 ++ .../EventDetailsPublicationTab.tsx | 4 ++- .../EventDetailsSchedulingTab.tsx | 3 +- .../EventDetailsWorkflowDetails.tsx | 4 +++ .../EventDetailsWorkflowErrors.tsx | 2 ++ .../EventDetailsWorkflowOperations.tsx | 2 ++ .../EventDetailsWorkflowSchedulingTab.tsx | 2 ++ .../EventDetailsWorkflowTab.tsx | 2 ++ .../ModalTabsAndPages/NewAccessPage.tsx | 2 ++ .../ModalTabsAndPages/NewProcessingPage.tsx | 1 + .../ModalTabsAndPages/NewSourcePage.tsx | 3 +- .../ModalTabsAndPages/NewTobiraPage.tsx | 2 ++ .../SeriesDetailsAccessTab.tsx | 3 +- .../StartTaskGeneralPage.tsx | 1 + .../StartTaskWorkflowPage.tsx | 4 +-- .../partials/modals/DeleteSeriesModal.tsx | 1 + .../modals/EditMetadataEventsModal.tsx | 1 + .../modals/EditScheduledEventsModal.tsx | 4 +-- .../events/partials/modals/EventDetails.tsx | 2 ++ .../events/partials/modals/SeriesDetails.tsx | 2 ++ .../partials/wizards/NewEventWizard.tsx | 4 +-- .../partials/wizards/NewSeriesWizard.tsx | 7 ++--- src/components/shared/DropDown.tsx | 10 +++---- src/components/shared/RegistrationModal.tsx | 3 +- src/components/shared/Stats.tsx | 13 ++++----- src/components/shared/Table.tsx | 15 +++++----- src/components/shared/TableFilters.tsx | 28 ++++++++++--------- .../modals/ResourceDetailsAccessPolicyTab.tsx | 4 +-- src/components/shared/wizard/FileUpload.tsx | 1 + .../shared/wizard/SelectContainer.tsx | 1 + src/components/statistics/Statistics.tsx | 8 ++---- src/i18n/i18n.ts | 1 + src/slices/seriesSlice.ts | 18 ++++++------ 41 files changed, 109 insertions(+), 80 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 70bca7e225..c36d230fd9 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -39,8 +39,8 @@ function App() { dispatch(fetchUserInfo()); }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + // Only run on mount + }, [dispatch]); return ( diff --git a/src/components/About.tsx b/src/components/About.tsx index 396a356758..c6deb04ed8 100644 --- a/src/components/About.tsx +++ b/src/components/About.tsx @@ -32,6 +32,7 @@ const About = () => { setAboutContent(t("ABOUT.NOCONTENT").toString()); }); }); + // Exclude t from the array, as it is not guaranteed to be stable // eslint-disable-next-line react-hooks/exhaustive-deps }, [location.pathname]); // Listen to changes in pathname diff --git a/src/components/Header.tsx b/src/components/Header.tsx index a2a2773f91..64d72ae903 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -57,10 +57,6 @@ const Header = () => { const orgProperties = useAppSelector(state => getOrgProperties(state)); const displayTerms = (orgProperties["org.opencastproject.admin.display_terms"] || "false").toLowerCase() === "true"; - const loadHealthStatus = async () => { - await dispatch(fetchHealthStatus()); - }; - const hideMenuHelp = () => { setMenuHelp(false); }; @@ -120,6 +116,9 @@ const Header = () => { } }; + const loadHealthStatus = async () => { + await dispatch(fetchHealthStatus()); + }; // Fetching health status information at mount loadHealthStatus().then(r => console.info(r)); @@ -133,8 +132,8 @@ const Header = () => { clearInterval(interval); window.removeEventListener("mousedown", handleClickOutside); }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + // Only run on mount + }, [dispatch]); useEffect(() => { if (!user) { return; } diff --git a/src/components/events/Events.tsx b/src/components/events/Events.tsx index 0ade511d14..07eb0863b6 100644 --- a/src/components/events/Events.tsx +++ b/src/components/events/Events.tsx @@ -54,8 +54,7 @@ const Events = () => { useEffect(() => { // disable actions button dispatch(setShowActions(false)); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [location.hash]); + }, [dispatch, location.hash]); const onNewEventModal = async () => { await Promise.all([ diff --git a/src/components/events/Series.tsx b/src/components/events/Series.tsx index 819f8c7c2a..8f99282a06 100644 --- a/src/components/events/Series.tsx +++ b/src/components/events/Series.tsx @@ -38,8 +38,7 @@ const Series = () => { useEffect(() => { // disable actions button dispatch(showActionsSeries(false)); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [location.hash]); + }, [dispatch, location.hash]); const onNewSeriesModal = async () => { await Promise.all([ diff --git a/src/components/events/partials/ModalTabsAndPages/DetailsTobiraTab.tsx b/src/components/events/partials/ModalTabsAndPages/DetailsTobiraTab.tsx index 8060923bae..afc6ac7624 100644 --- a/src/components/events/partials/ModalTabsAndPages/DetailsTobiraTab.tsx +++ b/src/components/events/partials/ModalTabsAndPages/DetailsTobiraTab.tsx @@ -42,8 +42,7 @@ const DetailsTobiraTab = ({ kind, id }: DetailsTobiraTabProps) => { // is removed when switching to another tab. dispatch(fetchEventDetailsTobira(id)); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [id]); + }, [dispatch, id, kind]); const [initialValues, setInitialValues] = useState({ breadcrumbs: [], diff --git a/src/components/events/partials/ModalTabsAndPages/EditScheduledEventsEditPage.tsx b/src/components/events/partials/ModalTabsAndPages/EditScheduledEventsEditPage.tsx index f81b06c9a5..6d1ffaa515 100644 --- a/src/components/events/partials/ModalTabsAndPages/EditScheduledEventsEditPage.tsx +++ b/src/components/events/partials/ModalTabsAndPages/EditScheduledEventsEditPage.tsx @@ -71,6 +71,8 @@ const EditScheduledEventsEditPage = ({ fetchNewScheduling: fetchEventInfos, setFormikValue: formik.setFieldValue, })); + // Dispatching the backend request should only happen when events change, + // and WILL change editedEvents // eslint-disable-next-line react-hooks/exhaustive-deps }, [formik.values.events]); diff --git a/src/components/events/partials/ModalTabsAndPages/EventDetailsAssetsTab.tsx b/src/components/events/partials/ModalTabsAndPages/EventDetailsAssetsTab.tsx index 5e54f2d8f8..e2f94cba11 100644 --- a/src/components/events/partials/ModalTabsAndPages/EventDetailsAssetsTab.tsx +++ b/src/components/events/partials/ModalTabsAndPages/EventDetailsAssetsTab.tsx @@ -85,6 +85,8 @@ const EventDetailsAssetsTab = ({ useEffect(() => { dispatch(removeNotificationWizardForm()); dispatch(fetchAssets(eventId)).then(); + // Only run on mount. + // Don't update when the id changes (which should not happen anyway) to avoid data inconsistencies // eslint-disable-next-line react-hooks/exhaustive-deps }, []); diff --git a/src/components/events/partials/ModalTabsAndPages/EventDetailsCommentsTab.tsx b/src/components/events/partials/ModalTabsAndPages/EventDetailsCommentsTab.tsx index 48f3dc1409..399147c731 100644 --- a/src/components/events/partials/ModalTabsAndPages/EventDetailsCommentsTab.tsx +++ b/src/components/events/partials/ModalTabsAndPages/EventDetailsCommentsTab.tsx @@ -47,6 +47,8 @@ const EventDetailsCommentsTab = ({ useEffect(() => { dispatch(fetchComments(eventId)); + // Only run on mount. + // Don't update when the id changes (which should not happen anyway) to avoid data inconsistencies // eslint-disable-next-line react-hooks/exhaustive-deps }, []); diff --git a/src/components/events/partials/ModalTabsAndPages/EventDetailsPublicationTab.tsx b/src/components/events/partials/ModalTabsAndPages/EventDetailsPublicationTab.tsx index a34d2a8654..ec6ad8f8d0 100644 --- a/src/components/events/partials/ModalTabsAndPages/EventDetailsPublicationTab.tsx +++ b/src/components/events/partials/ModalTabsAndPages/EventDetailsPublicationTab.tsx @@ -19,7 +19,9 @@ const EventDetailsPublicationTab = ({ const publications = useAppSelector(state => getPublications(state)); useEffect(() => { - dispatch(fetchEventPublications(eventId)).then(r => console.info(r)); + dispatch(fetchEventPublications(eventId)); + // Only run on mount. + // Don't update when the id changes (which should not happen anyway) to avoid data inconsistencies // eslint-disable-next-line react-hooks/exhaustive-deps }, []); diff --git a/src/components/events/partials/ModalTabsAndPages/EventDetailsSchedulingTab.tsx b/src/components/events/partials/ModalTabsAndPages/EventDetailsSchedulingTab.tsx index 86652f0e29..0d5b9e3ec2 100644 --- a/src/components/events/partials/ModalTabsAndPages/EventDetailsSchedulingTab.tsx +++ b/src/components/events/partials/ModalTabsAndPages/EventDetailsSchedulingTab.tsx @@ -100,7 +100,8 @@ const EventDetailsSchedulingTab = ({ startDate: sourceStartDate, endDate: endStartDate, deviceId: source.device.id, - })).then(); + })); + // Force an initial conflicts check // eslint-disable-next-line react-hooks/exhaustive-deps }, []); diff --git a/src/components/events/partials/ModalTabsAndPages/EventDetailsWorkflowDetails.tsx b/src/components/events/partials/ModalTabsAndPages/EventDetailsWorkflowDetails.tsx index 601c1f84a8..caca51ff6e 100644 --- a/src/components/events/partials/ModalTabsAndPages/EventDetailsWorkflowDetails.tsx +++ b/src/components/events/partials/ModalTabsAndPages/EventDetailsWorkflowDetails.tsx @@ -60,6 +60,8 @@ const EventDetailsWorkflowDetails = ({ } else { dispatch(fetchWorkflowDetails({ eventId, workflowId })); } + // Only run on mount. + // Don't update when the id changes (which should not happen anyway) to avoid data inconsistencies // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -350,6 +352,8 @@ const OperationsPreview = ({ // Unmount interval return () => clearInterval(fetchWorkflowOperationsInterval); + // Only run on mount. + // Don't update when the id changes (which should not happen anyway) to avoid data inconsistencies // eslint-disable-next-line react-hooks/exhaustive-deps }, []); diff --git a/src/components/events/partials/ModalTabsAndPages/EventDetailsWorkflowErrors.tsx b/src/components/events/partials/ModalTabsAndPages/EventDetailsWorkflowErrors.tsx index a9b6a600c9..0b96b0ff70 100644 --- a/src/components/events/partials/ModalTabsAndPages/EventDetailsWorkflowErrors.tsx +++ b/src/components/events/partials/ModalTabsAndPages/EventDetailsWorkflowErrors.tsx @@ -51,6 +51,8 @@ const EventDetailsWorkflowErrors = ({ if (workflowId) { dispatch(fetchWorkflowErrors({ eventId, workflowId })).then(); } + // Only run on mount. + // Don't update when the id changes (which should not happen anyway) to avoid data inconsistencies // eslint-disable-next-line react-hooks/exhaustive-deps }, []); diff --git a/src/components/events/partials/ModalTabsAndPages/EventDetailsWorkflowOperations.tsx b/src/components/events/partials/ModalTabsAndPages/EventDetailsWorkflowOperations.tsx index ecce2e6422..0e63000b1d 100644 --- a/src/components/events/partials/ModalTabsAndPages/EventDetailsWorkflowOperations.tsx +++ b/src/components/events/partials/ModalTabsAndPages/EventDetailsWorkflowOperations.tsx @@ -46,6 +46,8 @@ const EventDetailsWorkflowOperations = ({ // Unmount interval return () => clearInterval(fetchWorkflowOperationsInterval); + // Only run on mount. + // Don't update when the id changes (which should not happen anyway) to avoid data inconsistencies // eslint-disable-next-line react-hooks/exhaustive-deps }, []); diff --git a/src/components/events/partials/ModalTabsAndPages/EventDetailsWorkflowSchedulingTab.tsx b/src/components/events/partials/ModalTabsAndPages/EventDetailsWorkflowSchedulingTab.tsx index 20dad74e06..3b354af6e8 100644 --- a/src/components/events/partials/ModalTabsAndPages/EventDetailsWorkflowSchedulingTab.tsx +++ b/src/components/events/partials/ModalTabsAndPages/EventDetailsWorkflowSchedulingTab.tsx @@ -57,6 +57,8 @@ const EventDetailsWorkflowSchedulingTab = ({ useEffect(() => { dispatch(removeNotificationWizardForm()); dispatch(fetchWorkflows(eventId)).then(); + // Only run on mount. + // Don't update when the id changes (which should not happen anyway) to avoid data inconsistencies // eslint-disable-next-line react-hooks/exhaustive-deps }, []); diff --git a/src/components/events/partials/ModalTabsAndPages/EventDetailsWorkflowTab.tsx b/src/components/events/partials/ModalTabsAndPages/EventDetailsWorkflowTab.tsx index c2205117c0..38aa9369e9 100644 --- a/src/components/events/partials/ModalTabsAndPages/EventDetailsWorkflowTab.tsx +++ b/src/components/events/partials/ModalTabsAndPages/EventDetailsWorkflowTab.tsx @@ -55,6 +55,8 @@ const EventDetailsWorkflowTab = ({ useEffect(() => { dispatch(removeNotificationWizardForm()); dispatch(fetchWorkflows(eventId)).then(); + // Only run on mount. + // Don't update when the id changes (which should not happen anyway) to avoid data inconsistencies // eslint-disable-next-line react-hooks/exhaustive-deps }, []); diff --git a/src/components/events/partials/ModalTabsAndPages/NewAccessPage.tsx b/src/components/events/partials/ModalTabsAndPages/NewAccessPage.tsx index b2a7d9961b..4e0d866a15 100644 --- a/src/components/events/partials/ModalTabsAndPages/NewAccessPage.tsx +++ b/src/components/events/partials/ModalTabsAndPages/NewAccessPage.tsx @@ -81,6 +81,7 @@ const NewAccessPage = ({ if (initEventAclWithSeriesAcl && formik.values.metadata["dublincore/episode_isPartOf"]) { dispatch(fetchSeriesDetailsAcls(formik.values.metadata["dublincore/episode_isPartOf"])); } + // We only care about the series, not all metadata // eslint-disable-next-line react-hooks/exhaustive-deps }, [formik.values.metadata["dublincore/episode_isPartOf"], initEventAclWithSeriesAcl, dispatch]); @@ -89,6 +90,7 @@ const NewAccessPage = ({ if (initEventAclWithSeriesAcl && formik.values.metadata["dublincore/episode_isPartOf"] && seriesAcl) { formik.setFieldValue("policies", seriesAcl); } + // We only care to set "policies" if the seriesAcl updated // eslint-disable-next-line react-hooks/exhaustive-deps }, [initEventAclWithSeriesAcl, seriesAcl]); diff --git a/src/components/events/partials/ModalTabsAndPages/NewProcessingPage.tsx b/src/components/events/partials/ModalTabsAndPages/NewProcessingPage.tsx index 4cf30ad41b..2947ef6036 100644 --- a/src/components/events/partials/ModalTabsAndPages/NewProcessingPage.tsx +++ b/src/components/events/partials/ModalTabsAndPages/NewProcessingPage.tsx @@ -47,6 +47,7 @@ const NewProcessingPage = ({ if (workflowDef.length === 1) { setDefaultValues(workflowDef[0].id); } + // We only care to set default values if workflowDef changes // eslint-disable-next-line react-hooks/exhaustive-deps }, [workflowDef]); diff --git a/src/components/events/partials/ModalTabsAndPages/NewSourcePage.tsx b/src/components/events/partials/ModalTabsAndPages/NewSourcePage.tsx index 72fa4fafb6..5745e0c423 100644 --- a/src/components/events/partials/ModalTabsAndPages/NewSourcePage.tsx +++ b/src/components/events/partials/ModalTabsAndPages/NewSourcePage.tsx @@ -94,7 +94,8 @@ const NewSourcePage = ({ dispatch(fetchRecordings("inputs")); // validate form because dependent default values need to be checked - formik.validateForm().then(r => console.info(r)); + formik.validateForm(); + // Only run on mount // eslint-disable-next-line react-hooks/exhaustive-deps }, []); diff --git a/src/components/events/partials/ModalTabsAndPages/NewTobiraPage.tsx b/src/components/events/partials/ModalTabsAndPages/NewTobiraPage.tsx index 89265d5fbe..7273e3ea97 100644 --- a/src/components/events/partials/ModalTabsAndPages/NewTobiraPage.tsx +++ b/src/components/events/partials/ModalTabsAndPages/NewTobiraPage.tsx @@ -96,6 +96,7 @@ const NewTobiraPage = ({ valid = valid && check("warning", "TOBIRA_PATH_SEGMENT_INVALID", NOTIFICATION_CONTEXT_TOBIRA, () => ( newPage.segment.length <= 1 || [ + // We are explicitly checking that nothing is wrong with the path // eslint-disable-next-line no-control-regex /[\u0000-\u001F\u007F-\u009F]/u, /[\u00A0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]/u, @@ -158,6 +159,7 @@ const NewTobiraPage = ({ select(undefined); formik.setFieldValue("breadcrumbs", [...formik.values.breadcrumbs, currentPage]); } + // Should only trigger on page change // eslint-disable-next-line react-hooks/exhaustive-deps }, [currentPage]); diff --git a/src/components/events/partials/ModalTabsAndPages/SeriesDetailsAccessTab.tsx b/src/components/events/partials/ModalTabsAndPages/SeriesDetailsAccessTab.tsx index b39f8c68e0..d3443bcaee 100644 --- a/src/components/events/partials/ModalTabsAndPages/SeriesDetailsAccessTab.tsx +++ b/src/components/events/partials/ModalTabsAndPages/SeriesDetailsAccessTab.tsx @@ -36,8 +36,7 @@ const SeriesDetailsAccessTab = ({ useEffect(() => { dispatch(removeNotificationWizardForm()); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, [dispatch]); return ( ({ if (formik.values.events.length === 0) { formik.setFieldValue("events", selectedEvents); } + // Only run on mount // eslint-disable-next-line react-hooks/exhaustive-deps }, []); diff --git a/src/components/events/partials/ModalTabsAndPages/StartTaskWorkflowPage.tsx b/src/components/events/partials/ModalTabsAndPages/StartTaskWorkflowPage.tsx index 1701bcf479..9dc821b91f 100644 --- a/src/components/events/partials/ModalTabsAndPages/StartTaskWorkflowPage.tsx +++ b/src/components/events/partials/ModalTabsAndPages/StartTaskWorkflowPage.tsx @@ -37,14 +37,14 @@ const StartTaskWorkflowPage = ({ useEffect(() => { // Load workflow definitions for selecting dispatch(fetchWorkflowDef("tasks")); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, [dispatch]); // Preselect the first item useEffect(() => { if (workflowDef.length === 1) { setDefaultValues(workflowDef[0].id); } + // We only care to set default values if workflowDef changes // eslint-disable-next-line react-hooks/exhaustive-deps }, [workflowDef]); diff --git a/src/components/events/partials/modals/DeleteSeriesModal.tsx b/src/components/events/partials/modals/DeleteSeriesModal.tsx index 821d7f3ad5..adfb500283 100644 --- a/src/components/events/partials/modals/DeleteSeriesModal.tsx +++ b/src/components/events/partials/modals/DeleteSeriesModal.tsx @@ -62,6 +62,7 @@ const DeleteSeriesModal = ({ setSelectedSeries(series); } fetchData(); + // Only run on mount // eslint-disable-next-line react-hooks/exhaustive-deps }, []); diff --git a/src/components/events/partials/modals/EditMetadataEventsModal.tsx b/src/components/events/partials/modals/EditMetadataEventsModal.tsx index 9bb9773a19..8eba07e3fe 100644 --- a/src/components/events/partials/modals/EditMetadataEventsModal.tsx +++ b/src/components/events/partials/modals/EditMetadataEventsModal.tsx @@ -80,6 +80,7 @@ const EditMetadataEventsModal = ({ setLoading(false); } fetchData(); + // Only run on mount // eslint-disable-next-line react-hooks/exhaustive-deps }, []); diff --git a/src/components/events/partials/modals/EditScheduledEventsModal.tsx b/src/components/events/partials/modals/EditScheduledEventsModal.tsx index 8d02b1cdee..e6079da0f8 100644 --- a/src/components/events/partials/modals/EditScheduledEventsModal.tsx +++ b/src/components/events/partials/modals/EditScheduledEventsModal.tsx @@ -53,8 +53,8 @@ const EditScheduledEventsModal = ({ useEffect(() => { // Load recordings that can be used for input dispatch(fetchRecordings("inputs")); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + // Only run on mount + }, [dispatch]); type StepName = "general" | "edit" | "summary"; type Step = WizardStep & { diff --git a/src/components/events/partials/modals/EventDetails.tsx b/src/components/events/partials/modals/EventDetails.tsx index 6e190df462..b221e450e2 100644 --- a/src/components/events/partials/modals/EventDetails.tsx +++ b/src/components/events/partials/modals/EventDetails.tsx @@ -120,6 +120,8 @@ const EventDetails = ({ dispatch(fetchEventDetailsTobira(eventId)); + // Only run on mount. + // Don't update when the id changes (which should not happen anyway) to avoid data inconsistencies // eslint-disable-next-line react-hooks/exhaustive-deps }, []); diff --git a/src/components/events/partials/modals/SeriesDetails.tsx b/src/components/events/partials/modals/SeriesDetails.tsx index 323c430f93..bff322d2d9 100644 --- a/src/components/events/partials/modals/SeriesDetails.tsx +++ b/src/components/events/partials/modals/SeriesDetails.tsx @@ -69,6 +69,8 @@ const SeriesDetails = ({ dispatch(fetchSeriesStatistics(seriesId)); dispatch(fetchSeriesDetailsTobira(seriesId)); dispatch(setTobiraTabHierarchy("main")); + // Only run on mount. + // Don't update when the id changes (which should not happen anyway) to avoid data inconsistencies // eslint-disable-next-line react-hooks/exhaustive-deps }, []); diff --git a/src/components/events/partials/wizards/NewEventWizard.tsx b/src/components/events/partials/wizards/NewEventWizard.tsx index 1512c7021b..017adf7f25 100644 --- a/src/components/events/partials/wizards/NewEventWizard.tsx +++ b/src/components/events/partials/wizards/NewEventWizard.tsx @@ -48,9 +48,7 @@ const NewEventWizard = ({ useEffect(() => { dispatch(removeNotificationWizardForm()); - - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, [dispatch]); // Whether the ACL of a new event is initialized with the ACL of its series. let initEventAclWithSeriesAcl = true; diff --git a/src/components/events/partials/wizards/NewSeriesWizard.tsx b/src/components/events/partials/wizards/NewSeriesWizard.tsx index cde03290f4..1c80d7a4aa 100644 --- a/src/components/events/partials/wizards/NewSeriesWizard.tsx +++ b/src/components/events/partials/wizards/NewSeriesWizard.tsx @@ -48,16 +48,13 @@ const NewSeriesWizard = ({ useEffect(() => { dispatch(removeNotificationWizardForm()); - - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, [dispatch]); useEffect(() => { // This should set off a web request that will intentionally fail, in order // to check if tobira is available at all dispatch(fetchSeriesDetailsTobiraNew("")); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, [dispatch]); const themesEnabled = (orgProperties["admin.themes.enabled"] || "false").toLowerCase() === "true"; diff --git a/src/components/shared/DropDown.tsx b/src/components/shared/DropDown.tsx index 9cc87b895c..d41ba0f2dc 100644 --- a/src/components/shared/DropDown.tsx +++ b/src/components/shared/DropDown.tsx @@ -132,19 +132,19 @@ const DropDown = ({ */ const MenuList = (props: MenuListProps) => { const { children, maxHeight } = props; + const items = React.Children.toArray(children); return Array.isArray(children) ? (
@@ -156,7 +156,7 @@ const DropDown = ({ names, style, }: RowComponentProps<{ - names: string[]; + names: React.ReactNode[]; }>) { const name = names[index]; return
{name}
; diff --git a/src/components/shared/RegistrationModal.tsx b/src/components/shared/RegistrationModal.tsx index 3ccda2b80d..ec5638d112 100644 --- a/src/components/shared/RegistrationModal.tsx +++ b/src/components/shared/RegistrationModal.tsx @@ -76,8 +76,9 @@ const RegistrationModalContent = () => { }>(); useEffect(() => { - fetchRegistrationInfos().then(r => console.log(r)); + fetchRegistrationInfos(); fetchStatisticSummary(); + // Only run on mount // eslint-disable-next-line react-hooks/exhaustive-deps }, []); diff --git a/src/components/shared/Stats.tsx b/src/components/shared/Stats.tsx index 9451efa861..a36633616b 100644 --- a/src/components/shared/Stats.tsx +++ b/src/components/shared/Stats.tsx @@ -50,20 +50,19 @@ const Stats = () => { dispatch(loadEventsIntoTable()); }; - const loadStats = async () => { - // Fetching stats from server - await dispatch(fetchStats()); - }; - useEffect(() => { + const loadStats = async () => { + // Fetching stats from server + await dispatch(fetchStats()); + }; + // Load stats on mount loadStats(); const fetchEventsInterval = setInterval(() => loadStats(), 5000); return () => clearInterval(fetchEventsInterval); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, [dispatch]); return ( <> diff --git a/src/components/shared/Table.tsx b/src/components/shared/Table.tsx index c807c8afac..22828fb362 100644 --- a/src/components/shared/Table.tsx +++ b/src/components/shared/Table.tsx @@ -96,6 +96,7 @@ const Table = ({ allowLoadIntoTable = false; clearInterval(fetchResourceInterval); }; + // Only run on mount // eslint-disable-next-line react-hooks/exhaustive-deps }, [location.hash]); @@ -183,14 +184,14 @@ const MultiSelect = ({ selectAllCheckboxRef }: { selectAllCheckboxRef: React.Ref const selected = e.target.checked; dispatch(changeAllSelected(selected)); }; - useEffect(() => { - if (isNewEventAdded && multiSelect) { - if (selectAllCheckboxRef.current?.checked) { - selectAllCheckboxRef.current.checked = false; + + useEffect(() => { + if (isNewEventAdded && multiSelect) { + if (selectAllCheckboxRef.current?.checked) { + selectAllCheckboxRef.current.checked = false; } - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isNewEventAdded, multiSelect]); + } + }, [isNewEventAdded, multiSelect, selectAllCheckboxRef]); return ( <> diff --git a/src/components/shared/TableFilters.tsx b/src/components/shared/TableFilters.tsx index 7554d80918..2608d97ccc 100644 --- a/src/components/shared/TableFilters.tsx +++ b/src/components/shared/TableFilters.tsx @@ -69,6 +69,7 @@ const TableFilters = ({ useEffect(() => { dispatch(fetchFilters(resource)); + // Only run on mount // eslint-disable-next-line react-hooks/exhaustive-deps }, [location.hash]); @@ -145,26 +146,27 @@ const TableFilters = ({ } }; - // Apply the filter changes (in debounced) accomulated in handleChange, - // simply by going to first page and then load resources. - // This helps increase performance by reducing the number of calls to load resources. - const applyFilterChangesDebounced = async () => { - console.log("Applying filter changes with value: " + itemValue); - // No matter what, we go to page one. - dispatch(goToPage(0)); - // Reload of resource - await dispatch(loadResource()); - dispatch(loadResourceIntoTable()); - }; - useEffect(() => { + // Apply the filter changes (in debounced) accomulated in handleChange, + // simply by going to first page and then load resources. + // This helps increase performance by reducing the number of calls to load resources. + const applyFilterChangesDebounced = async () => { + console.log("Applying filter changes with value: " + itemValue); + // No matter what, we go to page one. + dispatch(goToPage(0)); + // Reload of resource + await dispatch(loadResource()); + dispatch(loadResourceIntoTable()); + }; + if (itemValue) { // Call to apply filter changes with 600MS debounce! const applyFilterChangesDebouncedTimeoutId = setTimeout(applyFilterChangesDebounced, 600); return () => clearTimeout(applyFilterChangesDebouncedTimeoutId); } - // eslint-disable-next-line react-hooks/exhaustive-deps + // Only run if the filter value changed + // eslint-disable-next-line react-hooks/exhaustive-deps }, [itemValue]); const handleDatepicker = (dates?: [Date | undefined | null, Date | undefined | null]) => { diff --git a/src/components/shared/modals/ResourceDetailsAccessPolicyTab.tsx b/src/components/shared/modals/ResourceDetailsAccessPolicyTab.tsx index 451d517e59..baf7e3c4c4 100644 --- a/src/components/shared/modals/ResourceDetailsAccessPolicyTab.tsx +++ b/src/components/shared/modals/ResourceDetailsAccessPolicyTab.tsx @@ -146,6 +146,7 @@ const ResourceDetailsAccessPolicyTab = ({ } fetchData().then(() => {}); + // Only run on mount // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -431,8 +432,7 @@ export const AccessPolicyTable = ({ useEffect(() => { dispatch(fetchAclDefaults()); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, [dispatch]); const dropdownOptions = useMemo(() => { return roles.length > 0 diff --git a/src/components/shared/wizard/FileUpload.tsx b/src/components/shared/wizard/FileUpload.tsx index cad6d0de1b..a8703e386e 100644 --- a/src/components/shared/wizard/FileUpload.tsx +++ b/src/components/shared/wizard/FileUpload.tsx @@ -65,6 +65,7 @@ const FileUpload = ({ // additional rerender which then triggers formik validation. useEffect(() => { formik.validateForm(); + // Only run validation if these values change // eslint-disable-next-line react-hooks/exhaustive-deps }, [formik.values.fileId, formik.values.fileName, loaded]); diff --git a/src/components/shared/wizard/SelectContainer.tsx b/src/components/shared/wizard/SelectContainer.tsx index e81a18d0c3..cce06b3e16 100644 --- a/src/components/shared/wizard/SelectContainer.tsx +++ b/src/components/shared/wizard/SelectContainer.tsx @@ -67,6 +67,7 @@ const SelectContainer = ({ setItems(initialItems); setDefaultItems(initialItems); + // Init on mount // eslint-disable-next-line react-hooks/exhaustive-deps }, []); diff --git a/src/components/statistics/Statistics.tsx b/src/components/statistics/Statistics.tsx index 4d5aeb7e27..e9b9337acf 100644 --- a/src/components/statistics/Statistics.tsx +++ b/src/components/statistics/Statistics.tsx @@ -34,12 +34,10 @@ const Statistics = () => { // fetch user information for organization id, then fetch statistics useEffect(() => { dispatch(fetchUserInfo()); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, [dispatch]); useEffect(() => { - dispatch(fetchStatisticsPageStatistics(organizationId)).then(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [organizationId]); + dispatch(fetchStatisticsPageStatistics(organizationId)); + }, [dispatch, organizationId]); /* generates file name for download-link for a statistic */ const statisticsCsvFileName = (statsTitle: string) => { diff --git a/src/i18n/i18n.ts b/src/i18n/i18n.ts index 4a8b19122e..cb2b2ea49e 100644 --- a/src/i18n/i18n.ts +++ b/src/i18n/i18n.ts @@ -57,6 +57,7 @@ const myFormatter: FormatterModule = { }); } + // If we are given any by the package, we can only really return any // eslint-disable-next-line @typescript-eslint/no-unsafe-return return value; }, diff --git a/src/slices/seriesSlice.ts b/src/slices/seriesSlice.ts index 9d5725c624..c7b35e3a10 100644 --- a/src/slices/seriesSlice.ts +++ b/src/slices/seriesSlice.ts @@ -222,10 +222,13 @@ export const postNewSeries = (params: { const access = prepareAccessPolicyRulesForPost(values.policies); // Tobira - const tobira: any = {}; + const tobira: { + parentPagePath?: string + newPages?: { name?: string, pathSegment: string }[] + } = {}; if (values.selectedPage && values.breadcrumbs) { - const existingPages: any[] = []; - const newPages: any[] = []; + const existingPages: TobiraPage[] = []; + const newPages: { name?: string, pathSegment: string }[] = []; values.breadcrumbs.concat(values.selectedPage).forEach(function (page: TobiraPage) { if (page.new) { newPages.push({ @@ -237,10 +240,8 @@ export const postNewSeries = (params: { } }); - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access - tobira["parentPagePath"] = existingPages.pop().path; - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - tobira["newPages"] = newPages; + tobira.parentPagePath = existingPages.pop()!.path; + tobira.newPages = newPages; } @@ -249,12 +250,11 @@ export const postNewSeries = (params: { options: unknown, access: typeof access, theme?: number, - tobira?: any + tobira?: typeof tobira } = { metadata: metadata, options: {}, access: access, - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment tobira: tobira, }; From 530f883148fc2a9c89274d4325b77d81cb31f369 Mon Sep 17 00:00:00 2001 From: Arnei Date: Thu, 18 Jun 2026 17:02:42 +0200 Subject: [PATCH 2/2] Fix a bunch of tx-expect-errors This removes a bunch of // @ts-expect-errors by fixing the respective type issues. --- .../EditScheduledEventsEditPage.tsx | 42 +++++++++++++++---- .../EditScheduledEventsGeneralPage.tsx | 5 +-- .../ModalTabsAndPages/NewAccessPage.tsx | 4 +- .../ModalTabsAndPages/NewProcessingPage.tsx | 2 +- .../StartTaskGeneralPage.tsx | 5 +-- .../StartTaskWorkflowPage.tsx | 2 +- .../partials/modals/DeleteEventsModal.tsx | 5 +-- .../partials/modals/DeleteSeriesModal.tsx | 5 +-- .../partials/wizards/NewEventSummary.tsx | 2 - .../partials/wizards/NewEventWizard.tsx | 3 -- .../partials/wizards/NewPlaylistSummary.tsx | 1 - .../partials/wizards/NewPlaylistWizard.tsx | 1 - .../partials/wizards/NewSeriesSummary.tsx | 2 - .../partials/wizards/NewSeriesWizard.tsx | 1 - .../partials/wizards/RenderWorkflowConfig.tsx | 2 +- .../summaryTables/MetadataSummaryTable.tsx | 6 +-- src/components/shared/MainNav.tsx | 10 ++--- src/components/shared/RegistrationModal.tsx | 6 +-- src/configs/modalConfig.ts | 6 ++- src/hooks/wizardHooks.ts | 3 +- src/selectors/tableSelectors.ts | 10 ++++- src/slices/seriesSlice.ts | 6 --- src/slices/tableSlice.ts | 12 +++++- 23 files changed, 80 insertions(+), 61 deletions(-) diff --git a/src/components/events/partials/ModalTabsAndPages/EditScheduledEventsEditPage.tsx b/src/components/events/partials/ModalTabsAndPages/EditScheduledEventsEditPage.tsx index 6d1ffaa515..ec117d72d6 100644 --- a/src/components/events/partials/ModalTabsAndPages/EditScheduledEventsEditPage.tsx +++ b/src/components/events/partials/ModalTabsAndPages/EditScheduledEventsEditPage.tsx @@ -95,21 +95,23 @@ const EditScheduledEventsEditPage = ({ */ const reduceGroupedEvent = (groupedEvents: EditedEvents[]) => { const result = groupedEvents.reduce((prev, curr) => { - for (const [key, value] of Object.entries(curr)) { - // TODO: This relies on the fact that the EditedEvent type only contains 'string' and 'string[]'. Improve on that. - if (typeof value === "string") { - // @ts-expect-error TS(7006): - prev[key as keyof EditedEvents] = prev[key as keyof EditedEvents] === curr[key as keyof EditedEvents] ? curr[key as keyof EditedEvents] : ""; - } else { - // @ts-expect-error TS(7006): - prev[key as keyof EditedEvents] = prev[key as keyof EditedEvents] === curr[key as keyof EditedEvents] ? curr[key as keyof EditedEvents] : []; - } + for (const key of Object.keys(curr) as Array) { + updateKey(key, prev, curr, defaultValuesEditedEvents); } return prev; }, lodash.cloneDeep(groupedEvents[0])); return result; }; + function updateKey( + key: K, + prev: EditedEvents, + curr: EditedEvents, + defaults: EditedEvents, + ) { + prev[key] = prev[key] === curr[key] ? curr[key] : defaults[key]; + } + const findSeriesName = (seriesOptions: { name: string, value: string }[], editedEvents: EditedEvents[]) => { const series = seriesOptions.find(e => e.value === reduceGroupedEvent(editedEvents).changedSeries); return series ? series.name : ""; @@ -428,4 +430,26 @@ const EditScheduledEventsEditPage = ({ ); }; +const defaultValuesEditedEvents: EditedEvents = { + changedDeviceInputs: [], + changedEndTimeHour: "", + changedEndTimeMinutes: "", + changedLocation: "", + changedSeries: "", + changedStartTimeHour: "", + changedStartTimeMinutes: "", + changedTitle: "", + changedWeekday: "MO", + deviceInputs: "", + endTimeHour: "", + endTimeMinutes: "", + eventId: "", + location: "", + series: "", + startTimeHour: "", + startTimeMinutes: "", + title: "", + weekday: "MO", +}; + export default EditScheduledEventsEditPage; diff --git a/src/components/events/partials/ModalTabsAndPages/EditScheduledEventsGeneralPage.tsx b/src/components/events/partials/ModalTabsAndPages/EditScheduledEventsGeneralPage.tsx index 60e20879d7..6719930df5 100644 --- a/src/components/events/partials/ModalTabsAndPages/EditScheduledEventsGeneralPage.tsx +++ b/src/components/events/partials/ModalTabsAndPages/EditScheduledEventsGeneralPage.tsx @@ -1,7 +1,7 @@ import { useEffect } from "react"; import { useTranslation } from "react-i18next"; import cn from "classnames"; -import { getSelectedRows } from "../../../../selectors/tableSelectors"; +import { getSelectedEvents } from "../../../../selectors/tableSelectors"; import { useSelectionChanges } from "../../../../hooks/wizardHooks"; import { getUserInformation } from "../../../../selectors/userInfoSelectors"; import { @@ -35,7 +35,7 @@ const EditScheduledEventsGeneralPage = ({ }) => { const { t } = useTranslation(); - const selectedRows = useAppSelector(state => getSelectedRows(state)); + const selectedRows = useAppSelector(state => getSelectedEvents(state)); const user = useAppSelector(state => getUserInformation(state)); const { @@ -43,7 +43,6 @@ const EditScheduledEventsGeneralPage = ({ allChecked, onChangeSelected, onChangeAllSelected, - // @ts-expect-error TS(7006): } = useSelectionChanges(formik, selectedRows); useEffect(() => { diff --git a/src/components/events/partials/ModalTabsAndPages/NewAccessPage.tsx b/src/components/events/partials/ModalTabsAndPages/NewAccessPage.tsx index 4e0d866a15..bac79efe26 100644 --- a/src/components/events/partials/ModalTabsAndPages/NewAccessPage.tsx +++ b/src/components/events/partials/ModalTabsAndPages/NewAccessPage.tsx @@ -25,10 +25,10 @@ import ModalContentTable from "../../../shared/modals/ModalContentTable"; */ interface RequiredFormProps { metadata: { - "dublincore/episode_isPartOf": string, + "dublincore/episode_isPartOf"?: string, }, policies: TransformedAcl[], - aclTemplate: string, + aclTemplate: string, // For TemplateSelector // theme: string, } diff --git a/src/components/events/partials/ModalTabsAndPages/NewProcessingPage.tsx b/src/components/events/partials/ModalTabsAndPages/NewProcessingPage.tsx index 2947ef6036..ee710ce29c 100644 --- a/src/components/events/partials/ModalTabsAndPages/NewProcessingPage.tsx +++ b/src/components/events/partials/ModalTabsAndPages/NewProcessingPage.tsx @@ -17,6 +17,7 @@ import ModalContentTable from "../../../shared/modals/ModalContentTable"; interface RequiredFormProps { sourceMode: string, processingWorkflow: string, + configuration?: { [key: string]: unknown } // For RenderWorkflowConfig } const NewProcessingPage = ({ @@ -119,7 +120,6 @@ const NewProcessingPage = ({ ) : null} diff --git a/src/components/events/partials/ModalTabsAndPages/StartTaskGeneralPage.tsx b/src/components/events/partials/ModalTabsAndPages/StartTaskGeneralPage.tsx index 0104f924f9..7941539289 100644 --- a/src/components/events/partials/ModalTabsAndPages/StartTaskGeneralPage.tsx +++ b/src/components/events/partials/ModalTabsAndPages/StartTaskGeneralPage.tsx @@ -2,7 +2,7 @@ import { useEffect } from "react"; import { useTranslation } from "react-i18next"; import Notifications from "../../../shared/Notifications"; import cn from "classnames"; -import { getSelectedRows } from "../../../../selectors/tableSelectors"; +import { getSelectedEvents } from "../../../../selectors/tableSelectors"; import { useSelectionChanges } from "../../../../hooks/wizardHooks"; import { checkValidityStartTaskEventSelection, @@ -34,14 +34,13 @@ const StartTaskGeneralPage = ({ }) => { const { t } = useTranslation(); - const selectedRows = useAppSelector(state => getSelectedRows(state)); + const selectedRows = useAppSelector(state => getSelectedEvents(state)); const { selectedEvents, allChecked, onChangeSelected, onChangeAllSelected, - // @ts-expect-error TS(7006): } = useSelectionChanges(formik, selectedRows); useEffect(() => { diff --git a/src/components/events/partials/ModalTabsAndPages/StartTaskWorkflowPage.tsx b/src/components/events/partials/ModalTabsAndPages/StartTaskWorkflowPage.tsx index 9dc821b91f..014736cb52 100644 --- a/src/components/events/partials/ModalTabsAndPages/StartTaskWorkflowPage.tsx +++ b/src/components/events/partials/ModalTabsAndPages/StartTaskWorkflowPage.tsx @@ -16,6 +16,7 @@ import ModalContentTable from "../../../shared/modals/ModalContentTable"; */ interface RequiredFormProps { workflow: string, + configuration?: { [key: string]: unknown } // For RenderWorkflowConfig } const StartTaskWorkflowPage = ({ @@ -101,7 +102,6 @@ const StartTaskWorkflowPage = ({ diff --git a/src/components/events/partials/modals/DeleteEventsModal.tsx b/src/components/events/partials/modals/DeleteEventsModal.tsx index 490c0c6773..77303ba0d4 100644 --- a/src/components/events/partials/modals/DeleteEventsModal.tsx +++ b/src/components/events/partials/modals/DeleteEventsModal.tsx @@ -1,6 +1,6 @@ import React, { useState } from "react"; import { useTranslation } from "react-i18next"; -import { getSelectedRows } from "../../../../selectors/tableSelectors"; +import { getSelectedEvents } from "../../../../selectors/tableSelectors"; import { useAppDispatch, useAppSelector } from "../../../../store"; import { deleteMultipleEvent } from "../../../../slices/eventSlice"; import { isEvent } from "../../../../slices/tableSlice"; @@ -19,13 +19,12 @@ const DeleteEventsModal = ({ const { t } = useTranslation(); const dispatch = useAppDispatch(); - const selectedRows = useAppSelector(state => getSelectedRows(state)); + const selectedRows = useAppSelector(state => getSelectedEvents(state)); const [allChecked, setAllChecked] = useState(true); const [selectedEvents, setSelectedEvents] = useState(selectedRows); const deleteSelectedEvents = () => { - // @ts-expect-error TS(7006): Type guarding array is hard dispatch(deleteMultipleEvent(selectedEvents)); close(); }; diff --git a/src/components/events/partials/modals/DeleteSeriesModal.tsx b/src/components/events/partials/modals/DeleteSeriesModal.tsx index adfb500283..b05b558edb 100644 --- a/src/components/events/partials/modals/DeleteSeriesModal.tsx +++ b/src/components/events/partials/modals/DeleteSeriesModal.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; -import { getSelectedRows } from "../../../../selectors/tableSelectors"; +import { getSelectedSeries } from "../../../../selectors/tableSelectors"; import cn from "classnames"; import { useAppDispatch, useAppSelector } from "../../../../store"; import { @@ -27,7 +27,7 @@ const DeleteSeriesModal = ({ const { t } = useTranslation(); const dispatch = useAppDispatch(); - const selectedRows = useAppSelector(state => getSelectedRows(state)); + const selectedRows = useAppSelector(state => getSelectedSeries(state)); const modifiedSelectedRows = selectedRows.map(row => { return { ...row, hasEvents: false }; }); @@ -67,7 +67,6 @@ const DeleteSeriesModal = ({ }, []); const deleteSelectedSeries = () => { - // @ts-expect-error TS(7006): Type guarding array is hard dispatch(deleteMultipleSeries(selectedSeries)); close(); }; diff --git a/src/components/events/partials/wizards/NewEventSummary.tsx b/src/components/events/partials/wizards/NewEventSummary.tsx index 43baec1036..19be0d6e41 100644 --- a/src/components/events/partials/wizards/NewEventSummary.tsx +++ b/src/components/events/partials/wizards/NewEventSummary.tsx @@ -103,7 +103,6 @@ const NewEventSummary = ({ {/* Summary metadata*/} @@ -112,7 +111,6 @@ const NewEventSummary = ({ {!metaDataExtendedHidden && ( diff --git a/src/components/events/partials/wizards/NewEventWizard.tsx b/src/components/events/partials/wizards/NewEventWizard.tsx index 017adf7f25..00a5c61a36 100644 --- a/src/components/events/partials/wizards/NewEventWizard.tsx +++ b/src/components/events/partials/wizards/NewEventWizard.tsx @@ -223,11 +223,8 @@ const NewEventWizard = ({ )} {steps[page].name === "access" && ( ({ diff --git a/src/components/events/partials/wizards/NewPlaylistWizard.tsx b/src/components/events/partials/wizards/NewPlaylistWizard.tsx index d641cad088..2afb3f24a7 100644 --- a/src/components/events/partials/wizards/NewPlaylistWizard.tsx +++ b/src/components/events/partials/wizards/NewPlaylistWizard.tsx @@ -152,7 +152,6 @@ const NewPlaylistWizard = ({ ({ {/* Summary metadata*/} @@ -56,7 +55,6 @@ const NewSeriesSummary = ({ {!metaDataExtendedHidden ? ( diff --git a/src/components/events/partials/wizards/NewSeriesWizard.tsx b/src/components/events/partials/wizards/NewSeriesWizard.tsx index 1c80d7a4aa..f200a635de 100644 --- a/src/components/events/partials/wizards/NewSeriesWizard.tsx +++ b/src/components/events/partials/wizards/NewSeriesWizard.tsx @@ -192,7 +192,6 @@ const NewSeriesWizard = ({ ({ diff --git a/src/components/events/partials/wizards/summaryTables/MetadataSummaryTable.tsx b/src/components/events/partials/wizards/summaryTables/MetadataSummaryTable.tsx index 0f0af7de8d..374d64c1e6 100644 --- a/src/components/events/partials/wizards/summaryTables/MetadataSummaryTable.tsx +++ b/src/components/events/partials/wizards/summaryTables/MetadataSummaryTable.tsx @@ -14,7 +14,7 @@ const MetadataSummaryTable = ({ header, }: { metadataCatalogs: MetadataCatalog[], - formikValues: { [key: string]: string | string[] | boolean | Date }, + formikValues: { [key: string]: unknown }, header: ParseKeys, }) => { const { t } = useTranslation(); @@ -26,7 +26,7 @@ const MetadataSummaryTable = ({ let metadata: { name: string, label: string, - value: string | string[] | boolean, + value: unknown, }[] = []; for (let i = 0; metadataFields.length > i; i++) { let fieldValue = @@ -82,7 +82,7 @@ const MetadataSummaryTable = ({ {Array.isArray(entry.value) ? entry.value.join(", ") - : entry.value} + : String(entry.value)} ))} diff --git a/src/components/shared/MainNav.tsx b/src/components/shared/MainNav.tsx index 40623d126f..b3f7f8f222 100644 --- a/src/components/shared/MainNav.tsx +++ b/src/components/shared/MainNav.tsx @@ -178,12 +178,12 @@ const MainNav = ({ if (linkMapItem?.links && linkMapItem.links.length > 1) { const arrToSort = linkMapItem.links; if (arrToSort != undefined && arrToSort.length > 1) { - arrToSort.forEach(item => { - // @ts-expect-error: TODO: Someone else can fix this - if (item.path === pathname) { item.tmpIndex = 0; } else { item.tmpIndex = 1; } + arrToSort.sort((a, b) => { + const aPriority = a.path === pathname ? 0 : 1; + const bPriority = b.path === pathname ? 0 : 1; + + return aPriority - bPriority; }); - // @ts-expect-error: TODO: Someone else can fix this - arrToSort.sort((a, b) => a.tmpIndex - b.tmpIndex); } } } diff --git a/src/components/shared/RegistrationModal.tsx b/src/components/shared/RegistrationModal.tsx index ec5638d112..c5b5fb3c26 100644 --- a/src/components/shared/RegistrationModal.tsx +++ b/src/components/shared/RegistrationModal.tsx @@ -684,8 +684,7 @@ const RegistrationModalContent = () => { {states[state].buttons.back && ( setState(states[state].nextState[5])} + onClick={() => setState(states[state].nextState[5] as keyof typeof states)} > {t("ADOPTER_REGISTRATION.MODAL.BACK")} @@ -701,8 +700,7 @@ const RegistrationModalContent = () => { {states[state].buttons.skip && ( setState(states[state].nextState[2])} + onClick={() => setState(states[state].nextState[2] as keyof typeof states)} > {t("ADOPTER_REGISTRATION.MODAL.SKIP")} diff --git a/src/configs/modalConfig.ts b/src/configs/modalConfig.ts index bbe35452c6..b8560caeb9 100644 --- a/src/configs/modalConfig.ts +++ b/src/configs/modalConfig.ts @@ -106,7 +106,7 @@ export const initialFormValuesNewSeries: { breadcrumbs: TobiraPage[], selectedPage?: TobiraPage, - aclTemplate?: string, + aclTemplate: string, metadata: { [key: string]: unknown } } = { policies: [ @@ -120,13 +120,14 @@ export const initialFormValuesNewSeries: { theme: "", breadcrumbs: [], selectedPage: undefined, + aclTemplate: "", metadata: {}, }; export const initialFormValuesNewPlaylist: { policies: TransformedAcl[], - aclTemplate?: string, + aclTemplate: string, metadata: { [key: string]: unknown }, entries: PlaylistEntry[], } = { @@ -138,6 +139,7 @@ export const initialFormValuesNewPlaylist: { actions: [], }, ], + aclTemplate: "", metadata: {}, entries: [], }; diff --git a/src/hooks/wizardHooks.ts b/src/hooks/wizardHooks.ts index 43555a302b..229bfc80ee 100644 --- a/src/hooks/wizardHooks.ts +++ b/src/hooks/wizardHooks.ts @@ -1,7 +1,6 @@ import { FormikProps } from "formik"; import { useEffect, useState } from "react"; import { Event } from "../slices/eventSlice"; -import { isEvent } from "../slices/tableSlice"; export const usePageFunctions = (initialPage: number) => { const [page, setPage] = useState(initialPage); @@ -65,7 +64,7 @@ export const useSelectionChanges = ( const onChangeSelected = (e: React.ChangeEvent, id: string) => { const selected = e.target.checked; const changedEvents = selectedEvents.map(event => { - if (isEvent(event) && event.id === id) { + if (event.id === id) { return { ...event, selected: selected, diff --git a/src/selectors/tableSelectors.ts b/src/selectors/tableSelectors.ts index 79d779fd80..5073625aee 100644 --- a/src/selectors/tableSelectors.ts +++ b/src/selectors/tableSelectors.ts @@ -1,6 +1,6 @@ import { createSelector } from "reselect"; import { RootState } from "../store"; -import { rowsSelectors, TableState } from "../slices/tableSlice"; +import { isEvent, isSeries, rowsSelectors, TableState } from "../slices/tableSlice"; /** * This file contains selectors regarding the table view @@ -31,3 +31,11 @@ export const getSelectedRows = createSelector( rowsSelectors.selectAll, rows => rows.filter(row => row.selected), ); +export const getSelectedEvents = createSelector( + rowsSelectors.selectAll, + rows => rows.filter(row => row.selected).filter(isEvent), +); +export const getSelectedSeries = createSelector( + rowsSelectors.selectAll, + rows => rows.filter(row => row.selected).filter(isSeries), +); diff --git a/src/slices/seriesSlice.ts b/src/slices/seriesSlice.ts index c7b35e3a10..d4b524d504 100644 --- a/src/slices/seriesSlice.ts +++ b/src/slices/seriesSlice.ts @@ -325,14 +325,8 @@ export const deleteSeries = (id: Series["id"]): AppThunk => dispatch => { // delete series with provided ids export const deleteMultipleSeries = ( series: { - contributors: string[], - createdBy: string, - creation_date: string, - hasEvents: false, id: string, - organizers: string[], selected: boolean, - title: string, }[], ): AppThunk => dispatch => { const data = []; diff --git a/src/slices/tableSlice.ts b/src/slices/tableSlice.ts index eef6905192..f4a348ba00 100644 --- a/src/slices/tableSlice.ts +++ b/src/slices/tableSlice.ts @@ -71,14 +71,22 @@ export function isRowSelectable(row: Row) { return false; } -export function isEvent(row: Row | Event | Series | Recording | Server | Job | Service | User | Group | AclResult | ThemeDetailsType): row is Event { +export function isEvent(row: Row): row is Row & Event { return (row as Event).event_status !== undefined; } -export function isSeries(row: Row | Event | Series | Recording | Server | Job | Service | User | Group | AclResult | ThemeDetailsType): row is Series { +// export function isEvent(row: Row | Event | Series | Recording | Server | Job | Service | User | Group | AclResult | ThemeDetailsType): row is Event { +// return (row as Event).event_status !== undefined; +// } + +export function isSeries(row: Row): row is Row & Series { return (row as Series).organizers !== undefined; } +// export function isSeries(row: Row | Event | Series | Recording | Server | Job | Service | User | Group | AclResult | ThemeDetailsType): row is Series { +// return (row as Series).organizers !== undefined; +// } + // TODO: Improve row typing. While this somewhat correctly reflects the current state of our code, it is rather annoying to work with. export type Row = { id: string, // For use with entityAdapter. Directly taken from event/series etc. if available