Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 26 additions & 67 deletions src/components/events/Events.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, { useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import cn from "classnames";
import { useLocation } from "react-router";
import TableFilters from "../shared/TableFilters";
import Stats from "../shared/Stats";
Expand Down Expand Up @@ -36,14 +35,11 @@ import {
} from "../../slices/eventSlice";
import EventDetailsModal from "./partials/modals/EventDetailsModal";
import { showModal } from "../../selectors/eventDetailsSelectors";
import ButtonLikeAnchor from "../shared/ButtonLikeAnchor";
import { eventsLinks } from "./partials/EventsNavigation";
import { Modal, ModalHandle } from "../shared/modals/Modal";
import TableActionDropdown from "../shared/TableActionDropdown";
import { resetTableProperties } from "../../slices/tableSlice";

// References for detecting a click outside of the container of the dropdown menu
const containerAction = React.createRef<HTMLDivElement>();

/**
* This component renders the table view of events
*/
Expand All @@ -53,7 +49,6 @@ const Events = () => {

const displayEventDetailsModal = useAppSelector(state => showModal(state));

const [displayActionMenu, setActionMenu] = useState(false);
const [displayNavigation, setNavigation] = useState(false);
const newEventModalRef = useRef<ModalHandle>(null);
const startTaskModalRef = useRef<ModalHandle>(null);
Expand Down Expand Up @@ -96,35 +91,16 @@ const Events = () => {
// call the function
loadEvents();

// Function for handling clicks outside of an open dropdown menu
const handleClickOutside = (e: MouseEvent) => {
if (
containerAction.current &&
!containerAction.current.contains(e.target as Node)
) {
setActionMenu(false);
}
};

// Fetch events every five seconds
let fetchEventsInterval = setInterval(() => loadEvents(), 5000);

// Event listener for handle a click outside of dropdown menu
window.addEventListener("mousedown", handleClickOutside);

return () => {
allowLoadIntoTable = false;
window.removeEventListener("mousedown", handleClickOutside);
clearInterval(fetchEventsInterval);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [location.hash]);

const handleActionMenu = (e: React.MouseEvent) => {
e.preventDefault();
setActionMenu(!displayActionMenu);
};

const onNewEventModal = async () => {
await dispatch(fetchEventMetadata());
await dispatch(fetchAssetUploadOptions());
Expand Down Expand Up @@ -215,48 +191,31 @@ const Events = () => {

<div className="controls-container">
<div className="filters-container">
<div
className={cn("drop-down-container", { disabled: !showActions })}
onClick={(e) => handleActionMenu(e)}
ref={containerAction}
>
<span>{t("BULK_ACTIONS.CAPTION")}</span>
{/* show dropdown if actions is clicked*/}
{displayActionMenu && (
<ul className="dropdown-ul">
{hasAccess("ROLE_UI_EVENTS_DELETE", user) && (
<li>
<ButtonLikeAnchor onClick={() => deleteModalRef.current?.open()}>
{t("BULK_ACTIONS.DELETE.EVENTS.CAPTION")}
</ButtonLikeAnchor>
</li>
)}
{hasAccess("ROLE_UI_TASKS_CREATE", user) && (
<li>
<ButtonLikeAnchor onClick={() => startTaskModalRef.current?.open()}>
{t("BULK_ACTIONS.SCHEDULE_TASK.CAPTION")}
</ButtonLikeAnchor>
</li>
)}
{hasAccess("ROLE_UI_EVENTS_DETAILS_SCHEDULING_EDIT", user) &&
hasAccess("ROLE_UI_EVENTS_DETAILS_METADATA_EDIT", user) && (
<li>
<ButtonLikeAnchor onClick={() => editScheduledEventsModalRef.current?.open()}>
{t("BULK_ACTIONS.EDIT_EVENTS.CAPTION")}
</ButtonLikeAnchor>
</li>
)}
{hasAccess("ROLE_UI_EVENTS_DETAILS_METADATA_EDIT", user) && (
<li>
<ButtonLikeAnchor onClick={() => editMetadataEventsModalRef.current?.open()}>
{t("BULK_ACTIONS.EDIT_EVENTS_METADATA.CAPTION")}
</ButtonLikeAnchor>
</li>
)}
</ul>
)}
</div>

<TableActionDropdown
actions={[
{
accessRole: ["ROLE_UI_EVENTS_DELETE"],
handleOnClick: () => deleteModalRef.current?.open(),
text: "BULK_ACTIONS.DELETE.EVENTS.CAPTION",
},
{
accessRole: ["ROLE_UI_TASKS_CREATE"],
handleOnClick: () => startTaskModalRef.current?.open(),
text: "BULK_ACTIONS.SCHEDULE_TASK.CAPTION",
},
{
accessRole: ["ROLE_UI_EVENTS_DETAILS_SCHEDULING_EDIT", "ROLE_UI_EVENTS_DETAILS_METADATA_EDIT"],
handleOnClick: () => editScheduledEventsModalRef.current?.open(),
text: "BULK_ACTIONS.EDIT_EVENTS.CAPTION",
},
{
accessRole: ["ROLE_UI_EVENTS_DETAILS_METADATA_EDIT"],
handleOnClick: () => editMetadataEventsModalRef.current?.open(),
text: "BULK_ACTIONS.EDIT_EVENTS_METADATA.CAPTION",
}
]}
disabled={!showActions}
/>
{/* Include filters component*/}
<TableFilters
loadResource={fetchEvents}
Expand Down
59 changes: 11 additions & 48 deletions src/components/events/Series.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, { useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import cn from "classnames";
import { useLocation } from "react-router";
import TableFilters from "../shared/TableFilters";
import Table from "../shared/Table";
Expand All @@ -16,8 +15,6 @@ import Header from "../Header";
import NavBar from "../NavBar";
import MainView from "../MainView";
import Footer from "../Footer";
import { getUserInformation } from "../../selectors/userInfoSelectors";
import { hasAccess } from "../../utils/utils";
import { useAppDispatch, useAppSelector } from "../../store";
import {
fetchSeries,
Expand All @@ -26,28 +23,22 @@ import {
showActionsSeries,
} from "../../slices/seriesSlice";
import { fetchSeriesDetailsTobiraNew } from "../../slices/seriesSlice";
import ButtonLikeAnchor from "../shared/ButtonLikeAnchor";
import { eventsLinks } from "./partials/EventsNavigation";
import { Modal, ModalHandle } from "../shared/modals/Modal";
import { availableHotkeys } from "../../configs/hotkeysConfig";
import TableActionDropdown from "../shared/TableActionDropdown";
import { resetTableProperties } from "../../slices/tableSlice";

// References for detecting a click outside of the container of the dropdown menu
const containerAction = React.createRef<HTMLDivElement>();

/**
* This component renders the table view of series
*/
const Series = () => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const [displayActionMenu, setActionMenu] = useState(false);
const [displayNavigation, setNavigation] = useState(false);
const newSeriesModalRef = useRef<ModalHandle>(null);
const deleteModalRef = useRef<ModalHandle>(null);

const user = useAppSelector(state => getUserInformation(state));

let location = useLocation();

const series = useAppSelector(state => getTotalSeries(state));
Expand Down Expand Up @@ -80,35 +71,16 @@ const Series = () => {
};
loadSeries();

// Function for handling clicks outside of an dropdown menu
const handleClickOutside = (e: MouseEvent) => {
if (
containerAction.current &&
!containerAction.current.contains(e.target as Node)
) {
setActionMenu(false);
}
};

// Fetch series every minute
let fetchSeriesInterval = setInterval(() => loadSeries(), 5000);

// Event listener for handle a click outside of dropdown menu
window.addEventListener("mousedown", handleClickOutside);

return () => {
allowLoadIntoTable = false;
window.removeEventListener("mousedown", handleClickOutside);
clearInterval(fetchSeriesInterval);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [location.hash]);

const handleActionMenu = (e: React.MouseEvent) => {
e.preventDefault();
setActionMenu(!displayActionMenu);
};

const onNewSeriesModal = async () => {
await dispatch(fetchSeriesMetadata());
await dispatch(fetchSeriesThemes());
Expand Down Expand Up @@ -155,25 +127,16 @@ const Series = () => {

<div className="controls-container">
<div className="filters-container">
<div
className={cn("drop-down-container", { disabled: !showActions })}
onClick={(e) => handleActionMenu(e)}
ref={containerAction}
>
<span>{t("BULK_ACTIONS.CAPTION")}</span>
{/* show dropdown if actions is clicked*/}
{displayActionMenu && (
<ul className="dropdown-ul">
{hasAccess("ROLE_UI_SERIES_DELETE", user) && (
<li>
<ButtonLikeAnchor onClick={() => deleteModalRef.current?.open()}>
{t("BULK_ACTIONS.DELETE.SERIES.CAPTION")}
</ButtonLikeAnchor>
</li>
)}
</ul>
)}
</div>
<TableActionDropdown
actions={[
{
accessRole: ["ROLE_UI_SERIES_DELETE"],
handleOnClick: () => deleteModalRef.current?.open(),
text: "BULK_ACTIONS.DELETE.SERIES.CAPTION",
},
]}
disabled={!showActions}
/>
{/* Include filters component */}
<TableFilters
loadResource={fetchSeries}
Expand Down
97 changes: 97 additions & 0 deletions src/components/shared/TableActionDropdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import React, { useEffect, useState } from "react";
import cn from "classnames";
import { hasAccess } from "../../utils/utils";
import { useTranslation } from "react-i18next";
import { useAppSelector } from "../../store";
import { getUserInformation } from "../../selectors/userInfoSelectors";
import { ParseKeys } from "i18next";
import ButtonLikeAnchor from "./ButtonLikeAnchor";

// References for detecting a click outside of the container of the dropdown menu
const containerAction = React.createRef<HTMLDivElement>();

/**
* A dropdown menu displayed above a table. The menu contains actions that can
* affect one or multiple selected entries in the table.
*/
const TableActionDropdown = ({
actions,
disabled=true,
}: {
actions: React.ComponentProps<typeof Action>[]
disabled: boolean
}) => {
const { t } = useTranslation();

const [displayActionMenu, setActionMenu] = useState(false);

useEffect(() => {
// Function for handling clicks outside of an open dropdown menu
const handleClickOutside = (e: MouseEvent) => {
if (
containerAction.current &&
!containerAction.current.contains(e.target as Node)
) {
setActionMenu(false);
}
};

// Event listener for handle a click outside of dropdown menu
window.addEventListener("mousedown", handleClickOutside);

return () => {
window.removeEventListener("mousedown", handleClickOutside);
};
}, []);

const handleActionMenu = (e: React.MouseEvent) => {
e.preventDefault();
setActionMenu(!displayActionMenu);
};

return (
<div
className={cn("drop-down-container", { disabled: disabled })}
onClick={(e) => handleActionMenu(e)}
ref={containerAction}
>
<span>{t("BULK_ACTIONS.CAPTION")}</span>
{/* show dropdown if actions is clicked*/}
{displayActionMenu && (
<ul className="dropdown-ul">
{actions.map((action =>
<Action
{...action}
/>
))}
</ul>
)}
</div>
)
};

const Action = ({
accessRole,
handleOnClick,
text,
}: {
accessRole: string[]
handleOnClick: (() => unknown) | void | undefined
text: ParseKeys
}) => {
const { t } = useTranslation();
const user = useAppSelector(state => getUserInformation(state));

return (
!!handleOnClick &&
accessRole.every((accessRole) => hasAccess(accessRole, user)) && (
<li>
<ButtonLikeAnchor onClick={handleOnClick}>
{t(text)}
</ButtonLikeAnchor>
</li>
)
)
}

export default TableActionDropdown;
Loading