From 54cfb590e33bbf5234ae8bbf97dce503b268be83 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 5f8ed04bab..345b19f1b8 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 0beb7fc2bb..90fef5c8b5 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 f281795240..528a7eecba 100644 --- a/src/components/events/partials/ModalTabsAndPages/EventDetailsWorkflowSchedulingTab.tsx +++ b/src/components/events/partials/ModalTabsAndPages/EventDetailsWorkflowSchedulingTab.tsx @@ -56,6 +56,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 42bc26d791..b03deff7a3 100644 --- a/src/components/events/partials/ModalTabsAndPages/NewProcessingPage.tsx +++ b/src/components/events/partials/ModalTabsAndPages/NewProcessingPage.tsx @@ -46,6 +46,7 @@ const NewProcessingPage = ({ if (workflowDefinitions.length === 1) { setDefaultValues(formik, workflowDefinitions, workflowDefinitions[0].id); } + // We only care to set default values if workflowDef changes // eslint-disable-next-line react-hooks/exhaustive-deps }, [workflowDefinitions]); diff --git a/src/components/events/partials/ModalTabsAndPages/NewSourcePage.tsx b/src/components/events/partials/ModalTabsAndPages/NewSourcePage.tsx index b9e6b306b5..fd495a2abe 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 bdd84ec708..3546a2310f 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 6bc8fd52f3..fab84dd3b0 100644 --- a/src/components/events/partials/ModalTabsAndPages/StartTaskWorkflowPage.tsx +++ b/src/components/events/partials/ModalTabsAndPages/StartTaskWorkflowPage.tsx @@ -36,14 +36,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 (workflowDefinitions.length === 1) { setDefaultValues(formik, workflowDefinitions, workflowDefinitions[0].id); } + // We only care to set default values if workflowDef changes // eslint-disable-next-line react-hooks/exhaustive-deps }, [workflowDefinitions]); 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 3c9c65ae95..e348a56bd4 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 4a60e796ab..fc7444bff1 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 fb7b9c90b3..669ac8aa4b 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 1fd01f3d81..96f2d3de6a 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 88b51804ca..e5823d7954 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 96b57cefeaec88505cae19c655dbf8bfdd054cd7 Mon Sep 17 00:00:00 2001 From: Arnei Date: Thu, 18 Jun 2026 17:02:42 +0200 Subject: [PATCH 2/2] :x 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 +- .../events/partials/PlaylistActionsCell.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 +++++- 24 files changed, 81 insertions(+), 62 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 b03deff7a3..02131a0180 100644 --- a/src/components/events/partials/ModalTabsAndPages/NewProcessingPage.tsx +++ b/src/components/events/partials/ModalTabsAndPages/NewProcessingPage.tsx @@ -16,6 +16,7 @@ import RenderWorkflowSelect from "../wizards/RenderWorkflowSelect"; interface RequiredFormProps { sourceMode: string, workflowId: string, + configuration?: { [key: string]: unknown } // For RenderWorkflowConfig } const NewProcessingPage = ({ @@ -83,7 +84,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 fab84dd3b0..2432c4073b 100644 --- a/src/components/events/partials/ModalTabsAndPages/StartTaskWorkflowPage.tsx +++ b/src/components/events/partials/ModalTabsAndPages/StartTaskWorkflowPage.tsx @@ -15,6 +15,7 @@ import RenderWorkflowSelect from "../wizards/RenderWorkflowSelect"; */ interface RequiredFormProps { workflowId: string, + configuration?: { [key: string]: unknown } // For RenderWorkflowConfig } const StartTaskWorkflowPage = ({ @@ -69,7 +70,6 @@ const StartTaskWorkflowPage = ({ diff --git a/src/components/events/partials/PlaylistActionsCell.tsx b/src/components/events/partials/PlaylistActionsCell.tsx index 539ba465e8..ddea0769c5 100644 --- a/src/components/events/partials/PlaylistActionsCell.tsx +++ b/src/components/events/partials/PlaylistActionsCell.tsx @@ -27,7 +27,7 @@ const PlaylistActionsCell = ({ return <> {/* playlist details */} showPlaylistDetailsModal()} + onClick={() => { showPlaylistDetailsModal(); }} className={"action-cell-button"} editAccessRole={"ROLE_UI_PLAYLISTS_DETAILS_VIEW"} > 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 449b1ca3bd..5cd38bf3c6 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 55f048b1bf..29aed6b9ea 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 38708c53b9..dd0d6e94eb 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