Skip to content

Commit 529113d

Browse files
committed
LifeCycle Policies - Squashed
The old PR for lifecycle policies was old, had many commits and many merges. Creating a squashed commit seemed like the simplest option to bring lifecycle policies from the old history to the new history.
1 parent 47bf603 commit 529113d

44 files changed

Lines changed: 4307 additions & 508 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

package-lock.json

Lines changed: 1648 additions & 438 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
"react-hotkeys-hook": "^5.2.4",
3333
"react-i18next": "^16.5.4",
3434
"react-icons": "^5.5.0",
35+
"react-js-cron": "^5.0.1",
3536
"react-redux": "^9.2.0",
3637
"react-router": "^7.13.0",
3738
"react-select": "^5.10.2",

src/App.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import Acls from "./components/users/Acls";
1515
import About from "./components/About";
1616
import { useAppDispatch } from "./store";
1717
import { fetchOcVersion, fetchUserInfo } from "./slices/userInfoSlice";
18+
import LifeCyclePolicies from "./components/events/LifeCyclePolicies";
1819
import { subscribeToAuthEvents } from "./utils/broadcastSync";
1920
import { useTableFilterStateValidation } from "./hooks/useTableFilterStateValidation";
2021

@@ -47,6 +48,8 @@ function App() {
4748

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

51+
<Route path={"/events/lifeCyclePolicies"} element={<LifeCyclePolicies />} />
52+
5053
<Route path={"/recordings/recordings"} element={<Recordings />} />
5154

5255
<Route path={"/systems/jobs"} element={<Jobs />} />
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import { useEffect, useRef, useState } from "react";
2+
import { useTranslation } from "react-i18next";
3+
import TableFilters from "../shared/TableFilters";
4+
import Table from "../shared/Table";
5+
import Notifications from "../shared/Notifications";
6+
import { loadLifeCyclePoliciesIntoTable } from "../../thunks/tableThunks";
7+
import { fetchFilters } from "../../slices/tableFilterSlice";
8+
import Header from "../Header";
9+
import NavBar from "../NavBar";
10+
import MainView from "../MainView";
11+
import Footer from "../Footer";
12+
import { useAppDispatch, useAppSelector } from "../../store";
13+
import { AsyncThunk } from "@reduxjs/toolkit";
14+
import { getTotalLifeCyclePolicies } from "../../selectors/lifeCycleSelectors";
15+
import { fetchLifeCyclePolicies } from "../../slices/lifeCycleSlice";
16+
import { lifeCyclePoliciesTemplateMap } from "../../configs/tableConfigs/lifeCyclePoliciesTableMap";
17+
import { fetchLifeCyclePolicyActions, fetchLifeCyclePolicyTargetTypes, fetchLifeCyclePolicyTimings } from "../../slices/lifeCycleDetailsSlice";
18+
import { ModalHandle } from "../shared/modals/Modal";
19+
import { eventsLinks } from "./partials/EventsNavigation";
20+
import { resetTableProperties } from "../../slices/tableSlice";
21+
import LifeCyclePolicyDetailsModal from "./partials/modals/LifeCyclePolicyDetailsModal";
22+
23+
/**
24+
* This component renders the table view of policies
25+
*/
26+
const LifeCyclePolicies = () => {
27+
const { t } = useTranslation();
28+
const dispatch = useAppDispatch();
29+
const [displayNavigation, setNavigation] = useState(false);
30+
const newPolicyModalRef = useRef<ModalHandle>(null);
31+
32+
const policiesTotal = useAppSelector(state => getTotalLifeCyclePolicies(state));
33+
34+
useEffect(() => {
35+
// State variable for interrupting the load function
36+
let allowLoadIntoTable = true;
37+
38+
// Clear table of previous data
39+
dispatch(resetTableProperties());
40+
41+
dispatch(fetchFilters("lifeCyclePolicies"));
42+
43+
// Load policies on mount
44+
const loadLifeCyclePolicies = async () => {
45+
// Fetching policies from server
46+
await dispatch(fetchLifeCyclePolicies());
47+
48+
// Load policies into table
49+
if (allowLoadIntoTable) {
50+
dispatch(loadLifeCyclePoliciesIntoTable());
51+
}
52+
};
53+
loadLifeCyclePolicies();
54+
55+
// Fetch policies repeatedly
56+
const fetchInterval = setInterval(loadLifeCyclePolicies, 5000);
57+
58+
return () => {
59+
allowLoadIntoTable = false;
60+
clearInterval(fetchInterval);
61+
};
62+
// eslint-disable-next-line react-hooks/exhaustive-deps
63+
}, []);
64+
65+
const showNewPolicyModal = async () => {
66+
await dispatch(fetchLifeCyclePolicyActions());
67+
await dispatch(fetchLifeCyclePolicyTargetTypes());
68+
await dispatch(fetchLifeCyclePolicyTimings());
69+
70+
newPolicyModalRef.current?.open();
71+
};
72+
73+
return (
74+
<>
75+
<Header />
76+
<NavBar
77+
displayNavigation={displayNavigation}
78+
setNavigation={setNavigation}
79+
navAriaLabel={"EVENTS.EVENTS.NAVIGATION.LABEL"}
80+
links={
81+
eventsLinks
82+
}
83+
create={{
84+
accessRole: "ROLE_UI_EVENTS_CREATE",
85+
onShowModal: showNewPolicyModal,
86+
text: "LIFECYCLE.POLICIES.TABLE.ADD_POLICY",
87+
resource: "lifecyclepolicy",
88+
}}
89+
>
90+
</NavBar>
91+
92+
<MainView open={displayNavigation}>
93+
{/* Include notifications component */}
94+
<Notifications context={"other"}/>
95+
96+
<div className="controls-container">
97+
{/* Include filters component */}
98+
{/* LifeCycle policies are not indexed, can't search or filter them */}
99+
{/* But if we don't include this component, the policies won't load on page load, because the first
100+
fetch request we send to the backend contains invalid params >.> */}
101+
<TableFilters
102+
loadResource={fetchLifeCyclePolicies as AsyncThunk<any, void, any>}
103+
loadResourceIntoTable={loadLifeCyclePoliciesIntoTable}
104+
resource={"lifeCyclePolicies"}
105+
/>
106+
107+
<h1>{t("LIFECYCLE.POLICIES.TABLE.CAPTION")}</h1>
108+
<h4>{t("TABLE_SUMMARY", { numberOfRows: policiesTotal })}</h4>
109+
</div>
110+
{/* Include table component */}
111+
<Table templateMap={lifeCyclePoliciesTemplateMap} />
112+
</MainView>
113+
<Footer />
114+
115+
{/* Include table modal */}
116+
<LifeCyclePolicyDetailsModal />
117+
</>
118+
);
119+
};
120+
121+
export default LifeCyclePolicies;

src/components/events/partials/EventsNavigation.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,9 @@ export const eventsLinks: {
1818
accessRole: "ROLE_UI_SERIES_VIEW",
1919
text: "EVENTS.EVENTS.NAVIGATION.SERIES",
2020
},
21+
{
22+
path: "/events/lifeCyclePolicies",
23+
accessRole: "ROLE_UI_LIFECYCLEPOLICIES_VIEW",
24+
text: "LIFECYCLE.NAVIGATION.POLICIES",
25+
},
2126
];
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { useAppDispatch } from "../../../store";
2+
import { deleteLifeCyclePolicy, LifeCyclePolicy } from "../../../slices/lifeCycleSlice";
3+
import { fetchLifeCyclePolicyDetails, openModal } from "../../../slices/lifeCycleDetailsSlice";
4+
import ButtonLikeAnchor from "../../shared/ButtonLikeAnchor";
5+
import { LuFileText } from "react-icons/lu";
6+
import { ActionCellDelete } from "../../shared/ActionCellDelete";
7+
8+
/**
9+
* This component renders the title cells of series in the table view
10+
*/
11+
const LifeCyclePolicyActionCell = ({
12+
row,
13+
}: {
14+
row: LifeCyclePolicy
15+
}) => {
16+
const dispatch = useAppDispatch();
17+
18+
const showLifeCyclePolicyDetails = async () => {
19+
await dispatch(fetchLifeCyclePolicyDetails(row.id));
20+
21+
dispatch(openModal(row));
22+
};
23+
24+
const deletingPolicy = (id: string) => {
25+
dispatch(deleteLifeCyclePolicy(id));
26+
};
27+
28+
return (
29+
<>
30+
{/* view details location/recording */}
31+
<ButtonLikeAnchor
32+
onClick={() => showLifeCyclePolicyDetails()}
33+
className={"action-cell-button"}
34+
editAccessRole={"ROLE_UI_LIFECYCLEPOLICY_DETAILS_VIEW"}
35+
// tooltipText={"LIFECYCLE.POLICIES.TABLE.TOOLTIP.DETAILS"} // Disabled due to performance concerns
36+
>
37+
<LuFileText />
38+
</ButtonLikeAnchor>
39+
40+
41+
{/* delete policy */}
42+
<ActionCellDelete
43+
editAccessRole={"ROLE_UI_LIFECYCLEPOLICY_DELETE"}
44+
// tooltipText={"LIFECYCLE.POLICIES.TABLE.TOOLTIP.DELETE"} // Disabled due to performance concerns
45+
resourceId={row.id}
46+
resourceName={row.title}
47+
resourceType={"LIFECYCLE_POLICY"}
48+
deleteMethod={deletingPolicy}
49+
/>
50+
</>
51+
);
52+
};
53+
54+
export default LifeCyclePolicyActionCell;
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { LifeCyclePolicy } from "../../../slices/lifeCycleSlice";
2+
3+
/**
4+
* This component renders the maintenance cells of servers in the table view
5+
*/
6+
const LifeCyclePolicyIsActiveCell = ({
7+
row,
8+
}: {
9+
row: LifeCyclePolicy
10+
}) => {
11+
12+
return (
13+
<input
14+
type="checkbox"
15+
checked={row.isActive}
16+
disabled={true}
17+
/>
18+
);
19+
};
20+
21+
export default LifeCyclePolicyIsActiveCell;
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import { useEffect } from "react";
2+
import { useTranslation } from "react-i18next";
3+
import { useAppDispatch, useAppSelector } from "../../../../store";
4+
import { getLifeCyclePoliciesForEvent } from "../../../../selectors/eventDetailsSelectors";
5+
import { fetchEventLifeCyclePolicies } from "../../../../slices/eventDetailsSlice";
6+
import ModalContentTable from "../../../shared/modals/ModalContentTable";
7+
import Notifications from "../../../shared/Notifications";
8+
import ButtonLikeAnchor from "../../../shared/ButtonLikeAnchor";
9+
import { LuChevronRight } from "react-icons/lu";
10+
import { useNavigate } from "react-router";
11+
import { fetchLifeCyclePolicyDetails, openModal } from "../../../../slices/lifeCycleDetailsSlice";
12+
import { LifeCyclePolicy } from "../../../../slices/lifeCycleSlice";
13+
14+
15+
/**
16+
* This component shows lifecycle policies that would affect the event
17+
*/
18+
const EventDetailsLifeCyclePolicy = ({
19+
eventId,
20+
}: {
21+
eventId: string,
22+
}) => {
23+
const { t } = useTranslation();
24+
const dispatch = useAppDispatch();
25+
const navigate = useNavigate();
26+
27+
const policies = useAppSelector(state => getLifeCyclePoliciesForEvent(state));
28+
29+
useEffect(() => {
30+
dispatch(fetchEventLifeCyclePolicies(eventId));
31+
// eslint-disable-next-line react-hooks/exhaustive-deps
32+
}, []);
33+
34+
const openPolicyDetails = async (policy: LifeCyclePolicy) => {
35+
await dispatch(fetchLifeCyclePolicyDetails(policy.id));
36+
dispatch(openModal(policy));
37+
navigate("/events/lifeCyclePolicies");
38+
};
39+
40+
return (
41+
<ModalContentTable
42+
modalBodyChildren={<Notifications context="not_corner" />}
43+
>
44+
{/* Disclaimer */}
45+
<div className="obj list-obj">
46+
<header className="no-expand">
47+
{t("EVENTS.EVENTS.DETAILS.LIFECYCLEPOLICIES.DISCLAIMER.TITLE")}
48+
</header>
49+
<div className="obj-container">
50+
<span>{t("EVENTS.EVENTS.DETAILS.LIFECYCLEPOLICIES.DISCLAIMER.MESSAGE")}</span>
51+
</div>
52+
</div>
53+
54+
<div className="obj tbl-container">
55+
{
56+
/* No policies message */
57+
policies.length === 0 && (
58+
<table className="main-tbl">
59+
<tr>
60+
<td colSpan={4}>
61+
{t("EVENTS.EVENTS.DETAILS.LIFECYCLEPOLICIES.EMPTY")}
62+
</td>
63+
</tr>
64+
</table>
65+
)
66+
}
67+
68+
{ policies.length !== 0 && (
69+
<div className="obj-container">
70+
<table className="main-tbl">
71+
<>
72+
<thead>
73+
<tr>
74+
<th>
75+
{t("EVENTS.EVENTS.DETAILS.LIFECYCLEPOLICIES.TABLE_TITLE")}
76+
</th>
77+
<th className="medium" />
78+
</tr>
79+
</thead>
80+
<tbody>
81+
{
82+
policies.map((policy, key) => (
83+
<tr key={key}>
84+
<td>
85+
{policy.title}
86+
</td>
87+
88+
{/* link to 'Details' sub-Tab */}
89+
<td>
90+
<ButtonLikeAnchor
91+
className="details-link"
92+
onClick={() => openPolicyDetails(policy)}
93+
>
94+
{t("EVENTS.EVENTS.DETAILS.MEDIA.DETAILS")}
95+
<LuChevronRight className="details-link-icon"/>
96+
</ButtonLikeAnchor>
97+
</td>
98+
</tr>
99+
))
100+
}
101+
</tbody>
102+
</>
103+
</table>
104+
</div>
105+
)}
106+
</div>
107+
</ModalContentTable>
108+
);
109+
};
110+
111+
export default EventDetailsLifeCyclePolicy;

src/components/events/partials/ModalTabsAndPages/EventDetailsWorkflowSchedulingTab.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ const EventDetailsWorkflowSchedulingTab = ({
187187
<div className="obj-container padded">
188188
{hasCurrentAgentAccess() &&
189189
isRoleWorkflowEdit &&
190+
formik.values.configuration &&
190191
!!workflowConfiguration &&
191192
!!workflowConfiguration.workflowId && (
192193
<div
@@ -197,7 +198,8 @@ const EventDetailsWorkflowSchedulingTab = ({
197198
workflowId={
198199
workflowConfiguration.workflowId
199200
}
200-
formik={formik}
201+
configuration={formik.values.configuration}
202+
configurationName={"configuration"}
201203
/>
202204
</div>
203205
)}

0 commit comments

Comments
 (0)