Skip to content

Commit

Permalink
Merge branch 'to-redux-toolkit-recordingDetails' of Arnei/opencast-ad…
Browse files Browse the repository at this point in the history
…min-interface into admin-ui-picard

Pull request #258

  Modernize redux: recordingDetailsSlice
  • Loading branch information
gregorydlogan committed Feb 16, 2024
2 parents 2e41bec + 1d75065 commit c619259
Show file tree
Hide file tree
Showing 14 changed files with 130 additions and 155 deletions.
25 changes: 0 additions & 25 deletions app/src/actions/recordingDetailsActions.ts

This file was deleted.

2 changes: 1 addition & 1 deletion app/src/components/recordings/Recordings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import cn from "classnames";
import TableFilters from "../shared/TableFilters";
import Table from "../shared/Table";
import Notifications from "../shared/Notifications";
import { recordingsTemplateMap } from "../../configs/tableConfigs/recordingsTableConfig";
import { recordingsTemplateMap } from "../../configs/tableConfigs/recordingsTableMap";
import { getTotalRecordings } from "../../selectors/recordingSelectors";
import { fetchRecordings } from "../../thunks/recordingThunks";
import { loadRecordingsIntoTable } from "../../thunks/tableThunks";
Expand Down
10 changes: 4 additions & 6 deletions app/src/components/recordings/partials/RecordingsActionCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import { connect } from "react-redux";
import ConfirmModal from "../../shared/ConfirmModal";
import RecordingDetailsModal from "./modal/RecordingDetailsModal";
import { deleteRecording } from "../../../thunks/recordingThunks";
import { fetchRecordingDetails } from "../../../thunks/recordingDetailsThunks";
import { getUserInformation } from "../../../selectors/userInfoSelectors";
import { hasAccess } from "../../../utils/utils";
import { useAppDispatch } from "../../../store";
import { fetchRecordingDetails } from "../../../slices/recordingDetailsSlice";

/**
* This component renders the action cells of recordings in the table view
Expand All @@ -16,12 +17,11 @@ const RecordingsActionCell = ({
row,
// @ts-expect-error TS(7031): Binding element 'deleteRecording' implicitly has a... Remove this comment to see the full error message
deleteRecording,
// @ts-expect-error TS(7031): Binding element 'fetchRecordingDetails' implicitly... Remove this comment to see the full error message
fetchRecordingDetails,
// @ts-expect-error TS(7031): Binding element 'user' implicitly has an 'any' typ... Remove this comment to see the full error message
user,
}) => {
const { t } = useTranslation();
const dispatch = useAppDispatch();

const [displayDeleteConfirmation, setDeleteConfirmation] = useState(false);
const [displayRecordingDetails, setRecordingDetails] = useState(false);
Expand All @@ -35,7 +35,7 @@ const RecordingsActionCell = ({
};

const showRecordingDetails = async () => {
await fetchRecordingDetails(row.name);
await dispatch(fetchRecordingDetails(row.name));

setRecordingDetails(true);
};
Expand Down Expand Up @@ -98,8 +98,6 @@ const mapStateToProps = (state) => ({
const mapDispatchToProps = (dispatch) => ({
// @ts-expect-error TS(7006): Parameter 'id' implicitly has an 'any' type.
deleteRecording: (id) => dispatch(deleteRecording(id)),
// @ts-expect-error TS(7006): Parameter 'name' implicitly has an 'any' type.
fetchRecordingDetails: (name) => dispatch(fetchRecordingDetails(name)),
});

export default connect(
Expand Down
16 changes: 5 additions & 11 deletions app/src/components/recordings/partials/modal/RecordingsDetails.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import React, { useState } from "react";
import { connect } from "react-redux";
import GeneralDetailsTab from "../wizards/GeneralDetailsTab";
import ConfigurationDetailsTab from "../wizards/ConfigurationDetailsTab";
import CapabilitiesDetailsTab from "../wizards/CapabilitiesDetailsTab";
import { getRecordingDetails } from "../../../../selectors/recordingDetailsSelectors";
import ModalNavigation from "../../../shared/modals/ModalNavigation";
import { useAppSelector } from "../../../../store";

/**
* This component manages the pages of the recording details
*/
const RecordingsDetails = ({
agent
}: any) => {
const RecordingsDetails: React.FC = () => {
const [page, setPage] = useState(0);

const agent = useAppSelector(state => getRecordingDetails(state));

// information about tabs
const tabs = [
{
Expand Down Expand Up @@ -52,10 +52,4 @@ const RecordingsDetails = ({
);
};

// get current state out of redux store
// @ts-expect-error TS(7006): Parameter 'state' implicitly has an 'any' type.
const mapStateToProps = (state) => ({
agent: getRecordingDetails(state),
});

export default connect(mapStateToProps)(RecordingsDetails);
export default RecordingsDetails;
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import React from "react";
import { useTranslation } from "react-i18next";
import { RecordingDetails } from "../../../../slices/recordingDetailsSlice";

/**
* This component renders details about the capabilities of a recording/capture agent
*/
const CapabilitiesDetailsTab = ({
agent
}: any) => {
const CapabilitiesDetailsTab: React.FC<{
agent: RecordingDetails
}> = ({
agent
}) => {
const { t } = useTranslation();

return (
Expand All @@ -25,7 +28,6 @@ const CapabilitiesDetailsTab = ({
<table className="main-tbl">
<tbody>
{/* Render table row for each capability item*/}
{/* @ts-expect-error TS(7006): Parameter 'item' implicitly has an 'any' type. */}
{agent.capabilities.map((item, key) => (
<tr key={key}>
<td>{item.key}</td>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import React from "react";
import { useTranslation } from "react-i18next";
import { RecordingDetails } from "../../../../slices/recordingDetailsSlice";

/**
* This component renders details about the configuration of a recording/capture agent
*/
const ConfigurationDetailsTab = ({
agent
}: any) => {
const ConfigurationDetailsTab: React.FC<{
agent: RecordingDetails
}> = ({
agent
}) => {
const { t } = useTranslation();

return (
Expand All @@ -25,7 +28,6 @@ const ConfigurationDetailsTab = ({
<table className="main-tbl">
<tbody>
{/* Render table row for each configuration item*/}
{/* @ts-expect-error TS(7006): Parameter 'item' implicitly has an 'any' type. */}
{agent.configuration.map((item, key) => (
<tr key={key}>
<td>{item.key}</td>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import React from "react";
import { useTranslation } from "react-i18next";
import { RecordingDetails } from "../../../../slices/recordingDetailsSlice";

/**
* This component renders details about a recording/capture agent
*/
const GeneralDetailsTab = ({
agent
}: any) => {
const GeneralDetailsTab: React.FC<{
agent: RecordingDetails
}> = ({
agent
}) => {
const { t } = useTranslation();

return (
Expand Down
16 changes: 0 additions & 16 deletions app/src/configs/tableConfigs/recordingsTableConfig.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
import RecordingsActionCell from "../../components/recordings/partials/RecordingsActionCell";
import RecordingsNameCell from "../../components/recordings/partials/RecordingsNameCell";
import RecordingsStatusCell from "../../components/recordings/partials/RecordingsStatusCell";
import RecordingsUpdateCell from "../../components/recordings/partials/RecordingsUpdateCell";

/**
* Config that contains the columns and further information regarding recordings. These are the information that never or hardly changes.
* That's why it is hard coded here and not fetched from server.
Expand Down Expand Up @@ -45,14 +40,3 @@ export const recordingsTableConfig = {
category: "recordings",
multiSelect: false,
};

/**
* This map contains the mapping between the template strings above and the corresponding react component.
* This helps to render different templates of cells more dynamically
*/
export const recordingsTemplateMap = {
RecordingsActionCell: RecordingsActionCell,
RecordingsNameCell: RecordingsNameCell,
RecordingsStatusCell: RecordingsStatusCell,
RecordingsUpdateCell: RecordingsUpdateCell,
};
15 changes: 15 additions & 0 deletions app/src/configs/tableConfigs/recordingsTableMap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import RecordingsActionCell from "../../components/recordings/partials/RecordingsActionCell";
import RecordingsNameCell from "../../components/recordings/partials/RecordingsNameCell";
import RecordingsStatusCell from "../../components/recordings/partials/RecordingsStatusCell";
import RecordingsUpdateCell from "../../components/recordings/partials/RecordingsUpdateCell";

/**
* This map contains the mapping between the template strings above and the corresponding react component.
* This helps to render different templates of cells more dynamically
*/
export const recordingsTemplateMap = {
RecordingsActionCell: RecordingsActionCell,
RecordingsNameCell: RecordingsNameCell,
RecordingsStatusCell: RecordingsStatusCell,
RecordingsUpdateCell: RecordingsUpdateCell,
};
59 changes: 0 additions & 59 deletions app/src/reducers/recordingDetailsReducer.ts

This file was deleted.

4 changes: 3 additions & 1 deletion app/src/selectors/recordingDetailsSelectors.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { RootState } from "../store";

/**
* This file contains selectors regarding details of a certain recording/capture agent
*/
export const getRecordingDetails = (state: any) => state.recordingDetails;
export const getRecordingDetails = (state: RootState) => state.recordingDetails;
83 changes: 83 additions & 0 deletions app/src/slices/recordingDetailsSlice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { PayloadAction, SerializedError, createAsyncThunk, createSlice } from '@reduxjs/toolkit'
import axios from 'axios';

/**
* This file contains redux reducer for actions affecting the state of a recording/capture agent
*/
export interface RecordingDetails {
name: string,
status: string,
update: string,
url: string,
capabilities: { key: string, value: string }[],
configuration: { key: string, value: string }[],
inputs: { id: string, value: string }[],
}

interface RecordingDetailsState extends RecordingDetails {
statusRecordingDetails: 'uninitialized' | 'loading' | 'succeeded' | 'failed',
errorRecordingDetails: SerializedError | null,
}

// Initial state of recording details in redux store
const initialState: RecordingDetailsState = {
statusRecordingDetails: 'uninitialized',
errorRecordingDetails: null,
name: "",
status: "",
update: "",
url: "",
capabilities: [],
configuration: [],
inputs: [],
};

// fetch details of certain recording from server
export const fetchRecordingDetails = createAsyncThunk('recordingDetails/fetchRecordingDetails', async (name: any, { getState }) => {
// Just make the async request here, and return the response.
// This will automatically dispatch a `pending` action first,
// and then `fulfilled` or `rejected` actions based on the promise.
const res = await axios.get(`/admin-ng/capture-agents/${name}`);
return res.data;
});

const recordingDetailsSlice = createSlice({
name: 'recordingDetails',
initialState,
reducers: {},
// These are used for thunks
extraReducers: builder => {
builder
.addCase(fetchRecordingDetails.pending, (state) => {
state.statusRecordingDetails = 'loading';
})
.addCase(fetchRecordingDetails.fulfilled, (state, action: PayloadAction<{
Name: RecordingDetailsState["name"],
Status: RecordingDetailsState["status"],
Update: RecordingDetailsState["update"],
URL: RecordingDetailsState["url"],
capabilities: RecordingDetailsState["capabilities"],
configuration: RecordingDetailsState["configuration"],
inputs: RecordingDetailsState["inputs"],
}>) => {
state.statusRecordingDetails = 'succeeded';
const recordingDetails = action.payload;
state.name = recordingDetails.Name;
state.status = recordingDetails.Status;
state.update = recordingDetails.Update;
state.url = recordingDetails.URL;
state.capabilities = recordingDetails.capabilities;
state.configuration = recordingDetails.configuration;
state.inputs = recordingDetails.inputs;
})
.addCase(fetchRecordingDetails.rejected, (state, action) => {
state.statusRecordingDetails = 'failed';
state.errorRecordingDetails = action.error;
});
}
});

// export const {} = recordingDetailsSlice.actions;

// Export the slice reducer as the default export
export default recordingDetailsSlice.reducer;
2 changes: 1 addition & 1 deletion app/src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { notifications } from "./reducers/notificationReducers";
import workflows from "./reducers/workflowReducers";
import eventDetails from "./reducers/eventDeatilsReducers";
import seriesDetails from "./reducers/seriesDetailsReducers";
import recordingDetails from "./reducers/recordingDetailsReducer";
import recordingDetails from "./slices/recordingDetailsSlice";
import userDetails from "./reducers/userDetailsReducer";
import groupDetails from "./reducers/groupDetailsReducer";
import aclDetails from "./reducers/aclDetailsReducer";
Expand Down
Loading

0 comments on commit c619259

Please sign in to comment.