Skip to content

Commit

Permalink
Display LifeCyclePolicies in UI (read-only)
Browse files Browse the repository at this point in the history
Adds a new tab to recordings called "LifeCycle
Policies". Much like other tabs this tab displays
information on its subject in a table format.
Unlike i.e. the events tab, the LifeCycle Policies
cannot be changed in any way, just be viewed.
Editing is supposed to be added at a later date.

Depends on PR opencast/opencast#6139
being merged to make any sense. Similarly, if you
would like to test this, your admin interface
should point to an Opencast with the PR
installed.
  • Loading branch information
Arnei committed Oct 10, 2024
1 parent a3129b9 commit b94add6
Show file tree
Hide file tree
Showing 21 changed files with 975 additions and 4 deletions.
3 changes: 3 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import Acls from "./components/users/Acls";
import About from "./components/About";
import { useAppDispatch } from "./store";
import { fetchOcVersion, fetchUserInfo } from "./slices/userInfoSlice";
import LifeCyclePolicies from "./components/events/LifeCyclePolicies";

function App() {
const dispatch = useAppDispatch();
Expand All @@ -41,6 +42,8 @@ function App() {

<Route path={"/events/series"} element={<Series />} />

<Route path={"/events/lifeCyclePolicies"} element={<LifeCyclePolicies />} />

<Route path={"/recordings/recordings"} element={<Recordings />} />

<Route path={"/systems/jobs"} element={<Jobs />} />
Expand Down
19 changes: 19 additions & 0 deletions src/components/events/Events.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import EditMetadataEventsModal from "./partials/modals/EditMetadataEventsModal";
import { eventsTemplateMap } from "../../configs/tableConfigs/eventsTableMap";
import {
loadEventsIntoTable,
loadLifeCyclePoliciesIntoTable,
loadSeriesIntoTable,
} from "../../thunks/tableThunks";
import { fetchFilters, fetchStats, editTextFilter } from "../../slices/tableFilterSlice";
Expand Down Expand Up @@ -43,6 +44,7 @@ import {
import { fetchSeries } from "../../slices/seriesSlice";
import EventDetailsModal from "./partials/modals/EventDetailsModal";
import { showModal } from "../../selectors/eventDetailsSelectors";
import { fetchLifeCyclePolicies } from "../../slices/lifeCycleSlice";

// References for detecting a click outside of the container of the dropdown menu
const containerAction = React.createRef<HTMLDivElement>();
Expand Down Expand Up @@ -99,6 +101,14 @@ const Events = () => {
dispatch(loadSeriesIntoTable());
};

const loadLifeCyclePolicies = async () => {
// Fetching policies from server
await dispatch(fetchLifeCyclePolicies());

// Load policies into table
dispatch(loadLifeCyclePoliciesIntoTable());
};

useEffect(() => {
if ("events" !== currentFilterType) {
dispatch(fetchFilters("events"))
Expand Down Expand Up @@ -230,6 +240,15 @@ const Events = () => {
{t("EVENTS.EVENTS.NAVIGATION.SERIES")}
</Link>
)}
{hasAccess("ROLE_UI_LIFECYCLEPOLICIES_VIEW", user) && (
<Link
to="/events/lifeCyclePolicies"
className={cn({ active: false })}
onClick={() => loadLifeCyclePolicies()}
>
{t("LIFECYCLE.NAVIGATION.POLICIES")}
</Link>
)}
</nav>

{/* Include status bar component*/}
Expand Down
151 changes: 151 additions & 0 deletions src/components/events/LifeCyclePolicies.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import React, { useEffect, useState } from "react";
import MainNav from "../shared/MainNav";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import cn from "classnames";
import TableFilters from "../shared/TableFilters";
import Table from "../shared/Table";
import Notifications from "../shared/Notifications";
import { loadEventsIntoTable, loadLifeCyclePoliciesIntoTable, loadSeriesIntoTable } from "../../thunks/tableThunks";
import { fetchFilters, editTextFilter, fetchStats } from "../../slices/tableFilterSlice";
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 { getCurrentFilterResource } from "../../selectors/tableFilterSelectors";
import { useAppDispatch, useAppSelector } from "../../store";
import { AsyncThunk } from "@reduxjs/toolkit";
import { AsyncThunkConfig } from "@reduxjs/toolkit/dist/createAsyncThunk";
import { getTotalLifeCyclePolicies } from "../../selectors/lifeCycleSelectors";
import { fetchLifeCyclePolicies } from "../../slices/lifeCycleSlice";
import { lifeCyclePoliciesTemplateMap } from "../../configs/tableConfigs/lifeCyclePoliciesTableMap";
import { fetchEvents } from "../../slices/eventSlice";
import { setOffset } from "../../slices/tableSlice";
import { fetchSeries } from "../../slices/seriesSlice";

/**
* This component renders the table view of policies
*/
const LifeCyclePolicies = () => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const [displayNavigation, setNavigation] = useState(false);

const user = useAppSelector(state => getUserInformation(state));
const policiesTotal = useAppSelector(state => getTotalLifeCyclePolicies(state));
const currentFilterType = useAppSelector(state => getCurrentFilterResource(state));

const loadEvents = async () => {
// Fetching stats from server
dispatch(fetchStats());

// Fetching events from server
await dispatch(fetchEvents());

// Load events into table
dispatch(loadEventsIntoTable());
};

const loadSeries = () => {
// Reset the current page to first page
dispatch(setOffset(0));

//fetching series from server
dispatch(fetchSeries());

//load series into table
dispatch(loadSeriesIntoTable());
};

const loadLifeCyclePolicies = async () => {
// Fetching policies from server
await dispatch(fetchLifeCyclePolicies());

// Load policies into table
dispatch(loadLifeCyclePoliciesIntoTable());
};

useEffect(() => {
if ("lifeCyclePolicies" !== currentFilterType) {
dispatch(fetchFilters("lifeCyclePolicies"));
}

// Reset text filter
dispatch(editTextFilter(""));

// Load policies on mount
loadLifeCyclePolicies().then((r) => console.info(r));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

const toggleNavigation = () => {
setNavigation(!displayNavigation);
};

return (
<>
<Header />
<NavBar>
{/* Include Burger-button menu*/}
<MainNav isOpen={displayNavigation} toggleMenu={toggleNavigation} />

<nav>
{hasAccess("ROLE_UI_EVENTS_VIEW", user) && (
<Link
to="/events/events"
className={cn({ active: false })}
onClick={() => loadEvents()}
>
{t("EVENTS.EVENTS.NAVIGATION.EVENTS")}
</Link>
)}
{hasAccess("ROLE_UI_SERIES_VIEW", user) && (
<Link
to="/events/series"
className={cn({ active: false })}
onClick={() => loadSeries()}
>
{t("EVENTS.EVENTS.NAVIGATION.SERIES")}
</Link>
)}
{hasAccess("ROLE_UI_LIFECYCLEPOLICIES_VIEW", user) && (
<Link
to="/events/lifeCyclePolicies"
className={cn({ active: true })}
onClick={() => loadLifeCyclePolicies()}
>
{t("LIFECYCLE.NAVIGATION.POLICIES")}
</Link>
)}
</nav>
</NavBar>

<MainView open={displayNavigation}>
{/* Include notifications component */}
<Notifications />

<div className="controls-container">
{/* Include filters component */}
{/* LifeCycle policies are not indexed, can't search or filter them */}
{/* But if we don't include this component, the policies won't load on page load, because the first
fetch request we send to the backend contains invalid params >.> */}
<TableFilters
loadResource={fetchLifeCyclePolicies as AsyncThunk<any, void, AsyncThunkConfig>}
loadResourceIntoTable={loadLifeCyclePoliciesIntoTable}
resource={"lifeCyclePolicies"}
/>

<h1>{t("LIFECYCLE.POLICIES.TABLE.CAPTION")}</h1>
<h4>{t("TABLE_SUMMARY", { numberOfRows: policiesTotal })}</h4>
</div>
{/* Include table component */}
<Table templateMap={lifeCyclePoliciesTemplateMap} />
</MainView>
<Footer />
</>
);
};

export default LifeCyclePolicies;
21 changes: 20 additions & 1 deletion src/components/events/Series.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import DeleteSeriesModal from "./partials/modals/DeleteSeriesModal";
import { seriesTemplateMap } from "../../configs/tableConfigs/seriesTableMap";
import {
loadEventsIntoTable,
loadLifeCyclePoliciesIntoTable,
loadSeriesIntoTable,
} from "../../thunks/tableThunks";
import { fetchFilters, fetchStats, editTextFilter } from "../../slices/tableFilterSlice";
Expand All @@ -34,6 +35,7 @@ import {
showActionsSeries,
} from "../../slices/seriesSlice";
import { fetchSeriesDetailsTobiraNew } from "../../slices/seriesSlice";
import { fetchLifeCyclePolicies } from "../../slices/lifeCycleSlice";

// References for detecting a click outside of the container of the dropdown menu
const containerAction = React.createRef<HTMLDivElement>();
Expand Down Expand Up @@ -79,6 +81,14 @@ const Series = () => {
dispatch(loadSeriesIntoTable());
};

const loadLifeCyclePolicies = async () => {
// Fetching policies from server
await dispatch(fetchLifeCyclePolicies());

// Load policies into table
dispatch(loadLifeCyclePoliciesIntoTable());
};

useEffect(() => {
if ("series" !== currentFilterType) {
dispatch(fetchFilters("series"))
Expand Down Expand Up @@ -186,8 +196,17 @@ const Series = () => {
{t("EVENTS.EVENTS.NAVIGATION.SERIES")}
</Link>
)}
{hasAccess("ROLE_UI_LIFECYCLEPOLICIES_VIEW", user) && (
<Link
to="/events/lifeCyclePolicies"
className={cn({ active: false })}
onClick={() => loadLifeCyclePolicies()}
>
{t("LIFECYCLE.NAVIGATION.POLICIES")}
</Link>
)}
</nav>

<div className="btn-group">
{hasAccess("ROLE_UI_SERIES_CREATE", user) && (
<button className="add" onClick={() => showNewSeriesModal()}>
Expand Down
62 changes: 62 additions & 0 deletions src/components/events/partials/LifeCyclePolicyActionCell.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { useAppDispatch, useAppSelector } from "../../../store";
import { Tooltip } from "../../shared/Tooltip";
import { LifeCyclePolicy } from "../../../slices/lifeCycleSlice";
import DetailsModal from "../../shared/modals/DetailsModal";
import LifeCyclePolicyDetails from "./modals/LifeCyclePolicyDetails";
import { hasAccess } from "../../../utils/utils";
import { getUserInformation } from "../../../selectors/userInfoSelectors";
import { fetchLifeCyclePolicyDetails } from "../../../slices/lifeCycleDetailsSlice";

/**
* This component renders the title cells of series in the table view
*/
const LifeCyclePolicyActionCell = ({
row,
}: {
row: LifeCyclePolicy
}) => {
const { t } = useTranslation();
const dispatch = useAppDispatch();

const [displayLifeCyclePolicyDetails, setLifeCyclePolicyDetails] = useState(false);

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

const showLifeCyclePolicyDetails = async () => {
await dispatch(fetchLifeCyclePolicyDetails(row.id));

setLifeCyclePolicyDetails(true);
};

const hideLifeCyclePolicyDetails = () => {
setLifeCyclePolicyDetails(false);
};

return (
<>
{/* view details location/recording */}
{hasAccess("ROLE_UI_LIFECYCLEPOLICY_DETAILS_VIEW", user) && (
<Tooltip title={t("LIFECYCLE.POLICIES.TABLE.TOOLTIP.DETAILS")}>
<button
className="button-like-anchor more"
onClick={() => showLifeCyclePolicyDetails()}
/>
</Tooltip>
)}

{displayLifeCyclePolicyDetails && (
<DetailsModal
handleClose={hideLifeCyclePolicyDetails}
title={row.title}
prefix={"LIFECYCLE.POLICIES.DETAILS.HEADER"}
>
<LifeCyclePolicyDetails />
</DetailsModal>
)}
</>
);
};

export default LifeCyclePolicyActionCell;
22 changes: 22 additions & 0 deletions src/components/events/partials/LifeCyclePolicyIsActiveCell.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from "react";
import { LifeCyclePolicy } from "../../../slices/lifeCycleSlice";

/**
* This component renders the maintenance cells of servers in the table view
*/
const LifeCyclePolicyIsActiveCell = ({
row,
}: {
row: LifeCyclePolicy
}) => {

return (
<input
type="checkbox"
checked={row.isActive}
disabled={true}
/>
);
};

export default LifeCyclePolicyIsActiveCell;
Loading

0 comments on commit b94add6

Please sign in to comment.