From 29afc47bb2bea6208a06855d1f398cc13ff61af9 Mon Sep 17 00:00:00 2001 From: Arnei Date: Wed, 8 Jan 2025 17:46:30 +0100 Subject: [PATCH] Reduce duplicate code for modals Introduces a generic modal component and replaces existing modal code with the new component. This intention here is to reduce code duplication and to make future generic changes to modals (i.e. focus trapping) easier. --- src/components/Header.tsx | 33 +- src/components/configuration/Themes.tsx | 20 +- .../partials/ThemesActionsCell.tsx | 49 +- .../partials/wizard/ThemeDetailsModal.tsx | 43 - src/components/events/Events.tsx | 80 +- src/components/events/Series.tsx | 48 +- .../events/partials/EventActionCell.tsx | 54 +- .../ModalTabsAndPages/DetailsTobiraTab.tsx | 14 +- .../events/partials/SeriesActionsCell.tsx | 62 +- .../partials/modals/DeleteEventsModal.tsx | 169 +-- .../partials/modals/DeleteSeriesModal.tsx | 182 ++- .../modals/EditMetadataEventsModal.tsx | 327 +++-- .../modals/EditScheduledEventsModal.tsx | 123 +- .../partials/modals/EmbeddingCodeModal.tsx | 166 +-- .../partials/modals/EventDetailsModal.tsx | 56 +- .../partials/modals/SeriesDetailsModal.tsx | 47 +- .../events/partials/modals/StartTaskModal.tsx | 119 +- .../partials/RecordingsActionCell.tsx | 43 +- .../partials/modal/RecordingDetailsModal.tsx | 52 +- src/components/shared/ConfirmModal.tsx | 131 +- src/components/shared/EditTableViewModal.tsx | 271 ++-- src/components/shared/HotKeyCheatSheet.tsx | 155 +-- src/components/shared/NewResourceModal.tsx | 106 +- src/components/shared/RegistrationModal.tsx | 1185 ++++++++--------- src/components/shared/Table.tsx | 18 +- src/components/shared/modals/Modal.tsx | 86 ++ src/components/users/Acls.tsx | 22 +- src/components/users/Groups.tsx | 20 +- src/components/users/Users.tsx | 20 +- .../users/partials/AclsActionsCell.tsx | 46 +- .../users/partials/GroupsActionsCell.tsx | 40 +- .../users/partials/UsersActionsCell.tsx | 46 +- .../users/partials/modal/AclDetailsModal.tsx | 59 - .../partials/modal/GroupDetailsModal.tsx | 48 +- .../users/partials/modal/UserDetailsModal.tsx | 60 - 35 files changed, 1845 insertions(+), 2155 deletions(-) delete mode 100644 src/components/configuration/partials/wizard/ThemeDetailsModal.tsx create mode 100644 src/components/shared/modals/Modal.tsx delete mode 100644 src/components/users/partials/modal/AclDetailsModal.tsx delete mode 100644 src/components/users/partials/modal/UserDetailsModal.tsx diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 6fc6a2a0e8..da4c6a0723 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { Link } from "react-router-dom"; import i18n from "../i18n/i18n"; @@ -23,6 +23,7 @@ import { UserInfoState } from "../slices/userInfoSlice"; import { Tooltip } from "./shared/Tooltip"; import { HiTranslate } from "react-icons/hi"; import { IconContext } from "react-icons"; +import { ModalHandle } from "./shared/modals/Modal"; // References for detecting a click outside of the container of the dropdown menus const containerLang = React.createRef(); @@ -52,8 +53,8 @@ const Header = () => { const [displayMenuUser, setMenuUser] = useState(false); const [displayMenuNotify, setMenuNotify] = useState(false); const [displayMenuHelp, setMenuHelp] = useState(false); - const [displayRegistrationModal, setRegistrationModal] = useState(false); - const [displayHotKeyCheatSheet, setHotKeyCheatSheet] = useState(false); + const registrationModalRef = useRef(null); + const hotKeyCheatSheetModalRef = useRef(null); const healthStatus = useAppSelector(state => getHealthStatus(state)); const errorCounter = useAppSelector(state => getErrorCount(state)); @@ -69,11 +70,7 @@ const Header = () => { }; const showRegistrationModal = () => { - setRegistrationModal(true); - }; - - const hideRegistrationModal = () => { - setRegistrationModal(false); + registrationModalRef.current?.open() }; const redirectToServices = async () => { @@ -85,15 +82,15 @@ const Header = () => { }; const showHotKeyCheatSheet = () => { - setHotKeyCheatSheet(true); - }; - - const hideHotKeyCheatSheet = () => { - setHotKeyCheatSheet(false); + hotKeyCheatSheetModalRef.current?.open() }; const toggleHotKeyCheatSheet = () => { - setHotKeyCheatSheet(!displayHotKeyCheatSheet); + if (hotKeyCheatSheetModalRef.current?.isOpen?.()) { + hotKeyCheatSheetModalRef.current?.close?.() + } else { + hotKeyCheatSheetModalRef.current?.open() + } }; useHotkeys( @@ -277,14 +274,10 @@ const Header = () => { {/* Adopters Registration Modal */} - {displayRegistrationModal && ( - - )} + {/* Hotkey Cheat Sheet */} - {displayHotKeyCheatSheet && ( - - )} + ); }; diff --git a/src/components/configuration/Themes.tsx b/src/components/configuration/Themes.tsx index fa4fab200c..f3c51e573c 100644 --- a/src/components/configuration/Themes.tsx +++ b/src/components/configuration/Themes.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import MainNav from "../shared/MainNav"; import { Link } from "react-router-dom"; @@ -20,6 +20,7 @@ import { hasAccess } from "../../utils/utils"; import { getCurrentFilterResource } from "../../selectors/tableFilterSelectors"; import { useAppDispatch, useAppSelector } from "../../store"; import { fetchThemes } from "../../slices/themeSlice"; +import { ModalHandle } from "../shared/modals/Modal"; /** * This component renders the table view of events @@ -31,7 +32,7 @@ const Themes = () => { const currentFilterType = useAppSelector(state => getCurrentFilterResource(state)); const [displayNavigation, setNavigation] = useState(false); - const [displayNewThemesModal, setNewThemesModal] = useState(false); + const newThemesModalRef = useRef(null); const user = useAppSelector(state => getUserInformation(state)); const themes = useAppSelector(state => getTotalThemes(state)); @@ -67,11 +68,11 @@ const Themes = () => { }; const showNewThemesModal = () => { - setNewThemesModal(true); + newThemesModalRef.current?.open(); }; const hideNewThemesModal = () => { - setNewThemesModal(false); + newThemesModalRef.current?.close?.(); }; return ( @@ -79,12 +80,11 @@ const Themes = () => {
{/* Display modal for new series if add series button is clicked */} - { displayNewThemesModal && - - } + {/* Include Burger-button menu*/} diff --git a/src/components/configuration/partials/ThemesActionsCell.tsx b/src/components/configuration/partials/ThemesActionsCell.tsx index 87925b8fb9..6b4a1b9031 100644 --- a/src/components/configuration/partials/ThemesActionsCell.tsx +++ b/src/components/configuration/partials/ThemesActionsCell.tsx @@ -1,7 +1,6 @@ -import React, { useState } from "react"; +import React, { useRef } from "react"; import { useTranslation } from "react-i18next"; import ConfirmModal from "../../shared/ConfirmModal"; -import ThemeDetailsModal from "./wizard/ThemeDetailsModal"; import { fetchThemeDetails, fetchUsage, @@ -11,6 +10,8 @@ import { hasAccess } from "../../../utils/utils"; import { useAppDispatch, useAppSelector } from "../../../store"; import { deleteTheme, ThemeDetailsType } from "../../../slices/themeSlice"; import { Tooltip } from "../../shared/Tooltip"; +import ThemeDetails from "./wizard/ThemeDetails"; +import { Modal, ModalHandle } from "../../shared/modals/Modal"; /** * This component renders the action cells of themes in the table view @@ -23,24 +24,24 @@ const ThemesActionsCell = ({ const { t } = useTranslation(); const dispatch = useAppDispatch(); - const [displayDeleteConfirmation, setDeleteConfirmation] = useState(false); - const [displayThemeDetails, setThemeDetails] = useState(false); + const deleteConfirmationModalRef = useRef(null); + const detailsModalRef = useRef(null); const user = useAppSelector(state => getUserInformation(state)); const hideDeleteConfirmation = () => { - setDeleteConfirmation(false); + deleteConfirmationModalRef.current?.close?.(); }; const hideThemeDetails = () => { - setThemeDetails(false); + detailsModalRef.current?.close?.(); }; const showThemeDetails = async () => { await dispatch(fetchThemeDetails(row.id)); await dispatch(fetchUsage(row.id)); - setThemeDetails(true); + detailsModalRef.current?.open(); }; const deletingTheme = (id: number) => { @@ -59,32 +60,34 @@ const ThemesActionsCell = ({ )} - {displayThemeDetails && ( - - )} + {/* themes details modal */} + + {/* component that manages tabs of theme details modal*/} + + {/* delete themes */} {hasAccess("ROLE_UI_THEMES_DELETE", user) && (
- - {/* component that manages tabs of theme details modal*/} - {/* */} - - - - ); -}; - -export default ThemeDetailsModal; diff --git a/src/components/events/Events.tsx b/src/components/events/Events.tsx index 0e1337a4f6..6c6ddad975 100644 --- a/src/components/events/Events.tsx +++ b/src/components/events/Events.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import cn from "classnames"; import { Link, useLocation } from "react-router-dom"; @@ -43,6 +43,7 @@ import { import { fetchSeries } from "../../slices/seriesSlice"; import EventDetailsModal from "./partials/modals/EventDetailsModal"; import { showModal } from "../../selectors/eventDetailsSelectors"; +import { Modal, ModalHandle } from "../shared/modals/Modal"; // References for detecting a click outside of the container of the dropdown menu const containerAction = React.createRef(); @@ -59,16 +60,11 @@ const Events = () => { const [displayActionMenu, setActionMenu] = useState(false); const [displayNavigation, setNavigation] = useState(false); - const [displayNewEventModal, setNewEventModal] = useState(false); - const [displayDeleteModal, setDeleteModal] = useState(false); - const [displayStartTaskModal, setStartTaskModal] = useState(false); - const [ - displayEditScheduledEventsModal, - setEditScheduledEventsModal, - ] = useState(false); - const [displayEditMetadataEventsModal, setEditMetadataEventsModal] = useState( - false - ); + const newEventModalRef = useRef(null); + const startTaskModalRef = useRef(null); + const deleteModalRef = useRef(null); + const editScheduledEventsModalRef = useRef(null); + const editMetadataEventsModalRef = useRef(null); const user = useAppSelector(state => getUserInformation(state)); const showActions = useAppSelector(state => isShowActions(state)); @@ -149,27 +145,27 @@ const Events = () => { await dispatch(fetchEventMetadata()); await dispatch(fetchAssetUploadOptions()); - setNewEventModal(true); + newEventModalRef.current?.open(); }; const hideNewEventModal = () => { - setNewEventModal(false); + newEventModalRef.current?.close?.(); }; const hideDeleteModal = () => { - setDeleteModal(false); + deleteModalRef.current?.close?.(); }; const hideStartTaskModal = () => { - setStartTaskModal(false); + startTaskModalRef.current?.close?.(); }; const hideEditScheduledEventsModal = () => { - setEditScheduledEventsModal(false); + editScheduledEventsModalRef.current?.close?.(); }; const hideEditMetadataEventsModal = () => { - setEditMetadataEventsModal(false); + editMetadataEventsModalRef.current?.close?.(); }; useHotkeys( @@ -187,26 +183,48 @@ const Events = () => { { /* Display modal for new event if add event button is clicked */ - !isFetchingAssetUploadOptions && displayNewEventModal && ( + !isFetchingAssetUploadOptions && ( ) } {/* Display bulk actions modal if one is chosen from dropdown */} - {displayDeleteModal && } - - {displayStartTaskModal && } - - {displayEditScheduledEventsModal && ( + + + + + + + + + - )} + - {displayEditMetadataEventsModal && ( + - )} + {/* Include Burger-button menu */} @@ -238,7 +256,7 @@ const Events = () => { )} - +
{hasAccess("ROLE_UI_EVENTS_CREATE", user) && ( )} {hasAccess("ROLE_UI_TASKS_CREATE", user) && (
  • -
  • @@ -281,14 +299,14 @@ const Events = () => { {hasAccess("ROLE_UI_EVENTS_DETAILS_SCHEDULING_EDIT", user) && hasAccess("ROLE_UI_EVENTS_DETAILS_METADATA_EDIT", user) && (
  • -
  • )} {hasAccess("ROLE_UI_EVENTS_DETAILS_METADATA_EDIT", user) && (
  • -
  • diff --git a/src/components/events/Series.tsx b/src/components/events/Series.tsx index 4dcce7f070..7685c8dfcc 100644 --- a/src/components/events/Series.tsx +++ b/src/components/events/Series.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useRef, useState } from "react"; import MainNav from "../shared/MainNav"; import { useTranslation } from "react-i18next"; import cn from "classnames"; @@ -22,9 +22,7 @@ import MainView from "../MainView"; import Footer from "../Footer"; import { getUserInformation } from "../../selectors/userInfoSelectors"; import { hasAccess } from "../../utils/utils"; -import { availableHotkeys } from "../../configs/hotkeysConfig"; import { getCurrentFilterResource } from "../../selectors/tableFilterSelectors"; -import { useHotkeys } from "react-hotkeys-hook"; import { useAppDispatch, useAppSelector } from "../../store"; import { fetchEvents } from "../../slices/eventSlice"; import { @@ -34,6 +32,7 @@ import { showActionsSeries, } from "../../slices/seriesSlice"; import { fetchSeriesDetailsTobiraNew } from "../../slices/seriesSlice"; +import { Modal, ModalHandle } from "../shared/modals/Modal"; // References for detecting a click outside of the container of the dropdown menu const containerAction = React.createRef(); @@ -46,8 +45,8 @@ const Series = () => { const dispatch = useAppDispatch(); const [displayActionMenu, setActionMenu] = useState(false); const [displayNavigation, setNavigation] = useState(false); - const [displayNewSeriesModal, setNewSeriesModal] = useState(false); - const [displayDeleteSeriesModal, setDeleteSeriesModal] = useState(false); + const newSeriesModalRef = useRef(null); + const deleteModalRef = useRef(null); const user = useAppSelector(state => getUserInformation(state)); const currentFilterType = useAppSelector(state => getCurrentFilterResource(state)); @@ -130,39 +129,36 @@ const Series = () => { await dispatch(fetchSeriesThemes()); await dispatch(fetchSeriesDetailsTobiraNew("/")); - setNewSeriesModal(true); + newSeriesModalRef.current?.open(); }; const hideNewSeriesModal = () => { - setNewSeriesModal(false); + newSeriesModalRef.current?.close?.(); }; const hideDeleteModal = () => { - setDeleteSeriesModal(false); + deleteModalRef.current?.close?.(); }; - useHotkeys( - availableHotkeys.general.NEW_SERIES.sequence, - () => showNewSeriesModal(), - { description: t(availableHotkeys.general.NEW_SERIES.description) ?? undefined }, - [showNewSeriesModal] - ); - return ( <>
    {/* Display modal for new series if add series button is clicked */} - { displayNewSeriesModal && - - } - - {displayDeleteSeriesModal && ( + + + - )} + {/* Include Burger-button menu */} @@ -187,7 +183,7 @@ const Series = () => { )} - +
    {hasAccess("ROLE_UI_SERIES_CREATE", user) && ( diff --git a/src/components/events/partials/EventActionCell.tsx b/src/components/events/partials/EventActionCell.tsx index 0654b63cf2..ab7996d6b1 100644 --- a/src/components/events/partials/EventActionCell.tsx +++ b/src/components/events/partials/EventActionCell.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useRef } from "react"; import { useTranslation } from "react-i18next"; import ConfirmModal from "../../shared/ConfirmModal"; import EmbeddingCodeModal from "./modals/EmbeddingCodeModal"; @@ -17,6 +17,7 @@ import { import { Event, deleteEvent } from "../../../slices/eventSlice"; import { Tooltip } from "../../shared/Tooltip"; import { openModal } from "../../../slices/eventDetailsSlice"; +import { Modal, ModalHandle } from "../../shared/modals/Modal"; /** * This component renders the action cells of events in the table view @@ -29,14 +30,14 @@ const EventActionCell = ({ const { t } = useTranslation(); const dispatch = useAppDispatch(); - const [displayDeleteConfirmation, setDeleteConfirmation] = useState(false); - const [displaySeriesDetailsModal, setSeriesDetailsModal] = useState(false); - const [displayEmbeddingCodeModal, setEmbeddingCodeModal] = useState(false); + const deleteConfirmationModalRef = useRef(null); + const seriesDetailsModalRef = useRef(null); + const embeddingCodeModalRef = useRef(null); const user = useAppSelector(state => getUserInformation(state)); const hideDeleteConfirmation = () => { - setDeleteConfirmation(false); + deleteConfirmationModalRef.current?.close?.() }; const deletingEvent = (id: string) => { @@ -44,19 +45,15 @@ const EventActionCell = ({ }; const hideEmbeddingCodeModal = () => { - setEmbeddingCodeModal(false); + embeddingCodeModalRef.current?.close?.(); }; const showEmbeddingCodeModal = () => { - setEmbeddingCodeModal(true); + embeddingCodeModalRef.current?.open(); }; const showSeriesDetailsModal = () => { - setSeriesDetailsModal(true); - }; - - const hideSeriesDetailsModal = () => { - setSeriesDetailsModal(false); + seriesDetailsModalRef.current?.open(); }; const onClickSeriesDetails = async () => { @@ -89,11 +86,11 @@ const EventActionCell = ({ return ( <> - {!!row.series && displaySeriesDetailsModal && ( + {!!row.series && ( )} @@ -122,22 +119,21 @@ const EventActionCell = ({ {hasAccess("ROLE_UI_EVENTS_DELETE", user) && (
    - -
    -
    -
    -
    -
    -

    {t("BULK_ACTIONS.DELETE_EVENTS_WARNING_LINE1")}

    -

    {t("BULK_ACTIONS.DELETE_EVENTS_WARNING_LINE2")}

    -
    - {/*todo: only show if scheduling Authorized*/} -
    -

    {t("BULK_ACTIONS.DELETE.EVENTS.UNAUTHORIZED")}

    -
    +
    +
    +
    +
    +
    +

    {t("BULK_ACTIONS.DELETE_EVENTS_WARNING_LINE1")}

    +

    {t("BULK_ACTIONS.DELETE_EVENTS_WARNING_LINE2")}

    +
    + {/*todo: only show if scheduling Authorized*/} +
    +

    {t("BULK_ACTIONS.DELETE.EVENTS.UNAUTHORIZED")}

    +
    -
    -
    -
    - {t("BULK_ACTIONS.DELETE.EVENTS.DELETE_EVENTS")} -
    - - - - + ))} + +
    +
    +
    +
    + {t("BULK_ACTIONS.DELETE.EVENTS.DELETE_EVENTS")} +
    + + + + + + + + + + {/* Repeat for each marked event*/} + {selectedEvents.map((event, key) => ( + + - - - - - {/* Repeat for each marked event*/} - {selectedEvents.map((event, key) => ( - - - - + + - - ))} - -
    + onChangeAllSelected(e)} + className="select-all-cbox" + /> + {t("EVENTS.EVENTS.TABLE.TITLE")}{t("EVENTS.EVENTS.TABLE.PRESENTERS")}
    onChangeAllSelected(e)} - className="select-all-cbox" + checked={event.selected} + onChange={(e) => onChangeSelected(e, isEvent(event) ? event.id : "")} /> - - {t("EVENTS.EVENTS.TABLE.TITLE")}{t("EVENTS.EVENTS.TABLE.PRESENTERS")}
    - onChangeSelected(e, isEvent(event) ? event.id : "")} - /> - {isEvent(event) && event.title} - {/* Repeat for each presenter*/} + {isEvent(event) && event.title} + {/* Repeat for each presenter*/} {/* @ts-expect-error TS(7006): Parameter 'presenter' implicitly has an 'any' type... Remove this comment to see the full error message */} - {event.presenters.map((presenter, key) => ( - - {presenter} - - ))} -
    -
    + {event.presenters.map((presenter, key) => ( + + {presenter} + + ))} + +
    +
    -
    - - -
    +
    + + +
    -
    - +
    ); }; diff --git a/src/components/events/partials/modals/DeleteSeriesModal.tsx b/src/components/events/partials/modals/DeleteSeriesModal.tsx index 1aaa578666..352cfe8908 100644 --- a/src/components/events/partials/modals/DeleteSeriesModal.tsx +++ b/src/components/events/partials/modals/DeleteSeriesModal.tsx @@ -125,112 +125,100 @@ const DeleteSeriesModal = ({ return ( <> -
    -
    -
    -
    - -
    -
    -
    -

    {t("BULK_ACTIONS.DELETE_SERIES_WARNING_LINE1")}

    -

    {t("BULK_ACTIONS.DELETE_SERIES_WARNING_LINE2")}

    -
    +
    +
    +
    +

    {t("BULK_ACTIONS.DELETE_SERIES_WARNING_LINE1")}

    +

    {t("BULK_ACTIONS.DELETE_SERIES_WARNING_LINE2")}

    +
    - {/* Only show if series not allowed to be deleted */} - {!isAllowed() && ( -
    -

    {t("BULK_ACTIONS.DELETE.SERIES.CANNOT_DELETE")}

    -
    - )} - -
    -
    -
    {t("EVENTS.SERIES.TABLE.CAPTION")}
    -
    - - - - + + ))} + +
    + {/* Only show if series not allowed to be deleted */} + {!isAllowed() && ( +
    +

    {t("BULK_ACTIONS.DELETE.SERIES.CANNOT_DELETE")}

    +
    + )} + +
    +
    +
    {t("EVENTS.SERIES.TABLE.CAPTION")}
    +
    + + + + + + + + + + + {/* Repeat for each marked series */} + {selectedSeries.map((series, key) => ( + + - - - - - - {/* Repeat for each marked series */} - {selectedSeries.map((series, key) => ( - - - - + + - {/* Only show check if row has events, else empty cell*/} - - - ))} - -
    + onChangeAllSelected(e)} + className="select-all-cbox" + /> + {t("EVENTS.SERIES.TABLE.TITLE")}{t("EVENTS.SERIES.TABLE.ORGANIZERS")}{t("EVENTS.SERIES.TABLE.HAS_EVENTS")}
    onChangeAllSelected(e)} - className="select-all-cbox" + name="selection" + checked={series.selected} + onChange={(e) => onChangeSelected(e, isSeries(series) ?series.id : "")} + className="child-cbox" /> - - {t("EVENTS.SERIES.TABLE.TITLE")}{t("EVENTS.SERIES.TABLE.ORGANIZERS")}{t("EVENTS.SERIES.TABLE.HAS_EVENTS")}
    - onChangeSelected(e, isSeries(series) ?series.id : "")} - className="child-cbox" - /> - {isSeries(series) && series.title} - {/*Repeat for each creator*/} + {isSeries(series) && series.title} + {/*Repeat for each creator*/} {/* @ts-expect-error TS(7006): Parameter 'organizer' implicitly has an 'any' type */} - {series.organizers.map((organizer, key) => ( - - {organizer} - - ))} - - {series.hasEvents && } -
    -
    + {series.organizers.map((organizer, key) => ( + + {organizer} + + ))} + + {/* Only show check if row has events, else empty cell*/} +
    + {series.hasEvents && } +
    - -
    - - -
    -
    +
    + +
    + + +
    ); }; diff --git a/src/components/events/partials/modals/EditMetadataEventsModal.tsx b/src/components/events/partials/modals/EditMetadataEventsModal.tsx index a0c6d7a2cd..838ed7cf48 100644 --- a/src/components/events/partials/modals/EditMetadataEventsModal.tsx +++ b/src/components/events/partials/modals/EditMetadataEventsModal.tsx @@ -17,8 +17,6 @@ import { updateBulkMetadata, } from "../../../../slices/eventSlice"; import { unwrapResult } from "@reduxjs/toolkit"; -import { useHotkeys } from "react-hotkeys-hook"; -import { availableHotkeys } from "../../../../configs/hotkeysConfig"; import { isEvent } from "../../../../slices/tableSlice"; /** @@ -50,13 +48,6 @@ const EditMetadataEventsModal = ({ const user = useAppSelector(state => getUserInformation(state)); - useHotkeys( - availableHotkeys.general.CLOSE_MODAL.sequence, - () => close(), - { description: t(availableHotkeys.general.CLOSE_MODAL.description) ?? undefined }, - [close], - ); - useEffect(() => { async function fetchData() { setLoading(true); @@ -148,189 +139,181 @@ const EditMetadataEventsModal = ({ return ( <> -
    -
    -
    -
    - - {/* Loading spinner */} - {loading && ( -
    -
    -
    - -
    + {/* Loading spinner */} + {loading && ( +
    +
    +
    +
    - )} +
    + )} - {/* Fatal error view */} - {!!fatalError && ( -
    -
    -
    -
    -

    - {t("BULK_ACTIONS.EDIT_EVENTS_METADATA.FATAL_ERROR", { - fatalError: fatalError, - })} -

    -
    + {/* Fatal error view */} + {!!fatalError && ( +
    +
    +
    +
    +

    + {t("BULK_ACTIONS.EDIT_EVENTS_METADATA.FATAL_ERROR", { + fatalError: fatalError, + })} +

    - )} +
    + )} - {/* todo: Request Errors View and Update Errors View (not quite sure what this is used for) */} + {/* todo: Request Errors View and Update Errors View (not quite sure what this is used for) */} - {!loading && fatalError === undefined && ( - handleSubmit(values)} - > - {(formik) => ( - <> -
    -
    -
    -
    + {!loading && fatalError === undefined && ( + handleSubmit(values)} + > + {(formik) => ( + <> +
    +
    +
    +
    + + {t( + "BULK_ACTIONS.EDIT_EVENTS_METADATA.EDIT.DESCRIPTION" + )} + +
    +
    +
    {t( - "BULK_ACTIONS.EDIT_EVENTS_METADATA.EDIT.DESCRIPTION" + "BULK_ACTIONS.EDIT_EVENTS_METADATA.EDIT.TABLE.CAPTION" )} -
    -
    -
    - - {t( - "BULK_ACTIONS.EDIT_EVENTS_METADATA.EDIT.TABLE.CAPTION" - )} - -
    -
    - - - - - - - - - {metadataFields.mergedMetadata.map( - (metadata, key) => - !metadata.readOnly && ( - - - - + + + + ) + )} + +
    - - {t( - "BULK_ACTIONS.EDIT_EVENTS_METADATA.EDIT.TABLE.FIELDS" - )} - - {t( - "BULK_ACTIONS.EDIT_EVENTS_METADATA.EDIT.TABLE.VALUES" - )} -
    - - onChangeSelected(e, metadata.id) - } - className="child-cbox" - /> - - {t(metadata.label)} - {metadata.required && ( - * - )} - - {/* Render single value or multi value input */} - {metadata.type === "mixed_text" && - !!metadata.collection && - metadata.collection.length !== 0 ? ( - - ) : ( - + +
    + + + + + + + + + {metadataFields.mergedMetadata.map( + (metadata, key) => + !metadata.readOnly && ( + + - ) - )} - -
    + + {t( + "BULK_ACTIONS.EDIT_EVENTS_METADATA.EDIT.TABLE.FIELDS" + )} + + {t( + "BULK_ACTIONS.EDIT_EVENTS_METADATA.EDIT.TABLE.VALUES" + )} +
    + -
    -
    + disabled={ + (!metadata.differentValues && + !metadata.selected) || + (metadata.required && + !metadata.selected) + } + onChange={(e) => + onChangeSelected(e, metadata.id) + } + className="child-cbox" + /> +
    + {t(metadata.label)} + {metadata.required && ( + * + )} + + {/* Render single value or multi value input */} + {metadata.type === "mixed_text" && + !!metadata.collection && + metadata.collection.length !== 0 ? ( + + ) : ( + + )} +
    +
    - {/* Buttons for cancel and submit */} -
    - - -
    + inactive: !( + formik.dirty && + formik.isValid && + hasAccess( + "ROLE_UI_EVENTS_DETAILS_METADATA_EDIT", + user + ) + ), + })} + > + {t("WIZARD.UPDATE")} + + + -
    - - )} - - )} -
    +
    + + )} + + )} ); }; diff --git a/src/components/events/partials/modals/EditScheduledEventsModal.tsx b/src/components/events/partials/modals/EditScheduledEventsModal.tsx index d64dd069aa..598896cc42 100644 --- a/src/components/events/partials/modals/EditScheduledEventsModal.tsx +++ b/src/components/events/partials/modals/EditScheduledEventsModal.tsx @@ -1,6 +1,5 @@ import React, { useEffect, useState } from "react"; import { Formik } from "formik"; -import { useTranslation } from "react-i18next"; import { initialFormValuesEditScheduledEvents } from "../../../../configs/modalConfig"; import WizardStepper from "../../../shared/wizard/WizardStepper"; import EditScheduledEventsGeneralPage from "../ModalTabsAndPages/EditScheduledEventsGeneralPage"; @@ -21,8 +20,6 @@ import { Conflict, } from "../../../../slices/eventSlice"; import { fetchRecordings } from "../../../../slices/recordingSlice"; -import { useHotkeys } from "react-hotkeys-hook"; -import { availableHotkeys } from "../../../../configs/hotkeysConfig"; import { Event } from "../../../../slices/eventSlice"; /** @@ -33,7 +30,6 @@ const EditScheduledEventsModal = ({ }: { close: () => void }) => { - const { t } = useTranslation(); const dispatch = useAppDispatch(); const inputDevices = useAppSelector(state => getRecordings(state)); @@ -55,13 +51,6 @@ const EditScheduledEventsModal = ({ const user = useAppSelector(state => getUserInformation(state)); - useHotkeys( - availableHotkeys.general.CLOSE_MODAL.sequence, - () => close(), - { description: t(availableHotkeys.general.CLOSE_MODAL.description) ?? undefined }, - [close], - ); - useEffect(() => { // Load recordings that can be used for input dispatch(fetchRecordings("inputs")); @@ -125,67 +114,59 @@ const EditScheduledEventsModal = ({ return ( <> -
    -
    -
    -
    - - {/* Initialize overall form */} - validateFormik(values)} - onSubmit={(values) => handleSubmit(values)} - > - {/* Render wizard pages depending on current value of page variable */} - {(formik) => { - // eslint-disable-next-line react-hooks/rules-of-hooks - useEffect(() => { - formik.validateForm().then(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [page]); + {/* Initialize overall form */} + validateFormik(values)} + onSubmit={(values) => handleSubmit(values)} + > + {/* Render wizard pages depending on current value of page variable */} + {(formik) => { + // eslint-disable-next-line react-hooks/rules-of-hooks + useEffect(() => { + formik.validateForm().then(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [page]); - return ( - <> - {/* Stepper that shows each step of wizard as header */} - -
    - {page === 0 && ( - - )} - {page === 1 && ( - - )} - {page === 2 && ( - - )} -
    - - ); - }} -
    -
    + return ( + <> + {/* Stepper that shows each step of wizard as header */} + +
    + {page === 0 && ( + + )} + {page === 1 && ( + + )} + {page === 2 && ( + + )} +
    + + ); + }} + ); }; diff --git a/src/components/events/partials/modals/EmbeddingCodeModal.tsx b/src/components/events/partials/modals/EmbeddingCodeModal.tsx index 271d96fe65..ed48d31557 100644 --- a/src/components/events/partials/modals/EmbeddingCodeModal.tsx +++ b/src/components/events/partials/modals/EmbeddingCodeModal.tsx @@ -1,8 +1,6 @@ import React, { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { getSourceURL } from "../../../../utils/embeddedCodeUtils"; -import { useHotkeys } from "react-hotkeys-hook"; -import { availableHotkeys } from "../../../../configs/hotkeysConfig"; /** * This component renders the embedding code modal @@ -21,13 +19,6 @@ const EmbeddingCodeModal = ({ const [currentSize, setCurrentSize] = useState("0x0"); const [showCopySuccess, setCopySuccess] = useState(false); - useHotkeys( - availableHotkeys.general.CLOSE_MODAL.sequence, - () => close(), - { description: t(availableHotkeys.general.CLOSE_MODAL.description) ?? undefined }, - [close], - ); - useEffect(() => { const fetchData = async () => { // get source url @@ -38,10 +29,6 @@ const EmbeddingCodeModal = ({ fetchData(); }, []); - const handleClose = () => { - close(); - }; - const copy = () => { let copyText = document.getElementById("social_embed-textarea") as HTMLTextAreaElement; if (copyText) { @@ -92,92 +79,81 @@ const EmbeddingCodeModal = ({ return ( <> -
    -
    -
    -
    - - {/* embed size buttons */} -
    - - - - + {/* embed size buttons */} +
    + + + + + +
    + + + {eventId} + + + {/* text area containing current iFrame code to copy*/} +
    +