From 659b63839802f6b12ae0edd268e4dd6cecaa4e7d Mon Sep 17 00:00:00 2001 From: Arnei Date: Fri, 16 Feb 2024 10:08:32 +0100 Subject: [PATCH 1/2] Properly close datepicker in details metadata The datepicker in details metadata would only close after a lot of clicking around. This was problematic, because the set date is not actually properly confirmed until after closing. This should help with that. --- .../components/shared/wizard/RenderField.tsx | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/app/src/components/shared/wizard/RenderField.tsx b/app/src/components/shared/wizard/RenderField.tsx index e3eb51f6d2..8021961b24 100644 --- a/app/src/components/shared/wizard/RenderField.tsx +++ b/app/src/components/shared/wizard/RenderField.tsx @@ -51,6 +51,7 @@ const RenderField = ({ setEditMode={setEditMode} form={form} showCheck={showCheck} + handleKeyDown={handleKeyDown} /> )} {metadataField.type === "text" && @@ -124,6 +125,7 @@ const RenderField = ({ editMode={editMode} setEditMode={setEditMode} showCheck={showCheck} + handleKeyDown={handleKeyDown} /> )} {metadataField.type === "boolean" && ( @@ -178,6 +180,8 @@ const EditableDateValue = ({ setEditMode, // @ts-expect-error TS(7031): Binding element 'showCheck' implicitly has an 'any... Remove this comment to see the full error message showCheck, +// @ts-expect-error TS(7031): + handleKeyDown }) => { const { t } = useTranslation(); @@ -188,7 +192,19 @@ const EditableDateValue = ({ value={typeof field.value === "string" ? parseISO(field.value) : field.value} onChange={(value) => setFieldValue(field.name, value)} onClose={() => setEditMode(false)} - slotProps={{ textField: { fullWidth: true } }} + slotProps={{ + textField: { + fullWidth: true, + onKeyDown: (event) => { + if (event.key === "Enter") { + handleKeyDown(event, "date") + } + }, + onBlur: (event) => { + setEditMode(false) + } + } + }} /> ) : ( @@ -372,6 +388,8 @@ const EditableSingleValueTime = ({ setEditMode, // @ts-expect-error TS(7031): Binding element 'showCheck' implicitly has an 'any... Remove this comment to see the full error message showCheck, + // @ts-expect-error TS(7031): Binding element 'handleKeyDown' implicitly has an ... Remove this comment to see the full error message + handleKeyDown, }) => { const { t } = useTranslation(); @@ -382,7 +400,19 @@ const EditableSingleValueTime = ({ value={parseISO(field.value)} onChange={(value) => setFieldValue(field.name, value)} onClose={() => setEditMode(false)} - slotProps={{ textField: { fullWidth: true } }} + slotProps={{ + textField: { + fullWidth: true, + onKeyDown: (event) => { + if (event.key === "Enter") { + handleKeyDown(event, "date") + } + }, + onBlur: (event) => { + setEditMode(false) + } + } + }} /> ) : ( From c4423685b8aada4cf006d4d573911db3c1af3656 Mon Sep 17 00:00:00 2001 From: Arnei Date: Fri, 8 Mar 2024 12:39:23 +0100 Subject: [PATCH 2/2] Type thunk parameters Attempts to type various redux thunk parameters. --- .../EditScheduledEventsEditPage.tsx | 4 +- .../EventDetailsWorkflowErrors.tsx | 2 +- .../EventDetailsWorkflowOperations.tsx | 2 +- .../modals/EditMetadataEventsModal.tsx | 22 +- .../modals/EditScheduledEventsModal.tsx | 4 +- .../partials/wizards/NewEventWizard.tsx | 3 +- app/src/slices/aclDetailsSlice.ts | 29 ++- app/src/slices/eventDetailsSlice.ts | 202 +++++++++++----- app/src/slices/eventSlice.ts | 215 ++++++++++++------ app/src/slices/groupDetailsSlice.ts | 7 +- app/src/slices/groupSlice.ts | 7 +- app/src/slices/jobSlice.ts | 3 +- app/src/slices/notificationSlice.ts | 9 +- app/src/slices/recordingDetailsSlice.ts | 2 +- app/src/slices/recordingSlice.ts | 9 +- app/src/slices/seriesDetailsSlice.ts | 84 ++++--- app/src/slices/seriesSlice.ts | 42 +++- app/src/slices/serverSlice.ts | 12 +- app/src/slices/serviceSlice.ts | 10 +- app/src/slices/themeDetailsSlice.ts | 9 +- app/src/slices/themeSlice.ts | 7 +- app/src/slices/userDetailsSlice.ts | 7 +- app/src/slices/userSlice.ts | 7 +- app/src/slices/workflowSlice.ts | 2 +- 24 files changed, 492 insertions(+), 208 deletions(-) diff --git a/app/src/components/events/partials/ModalTabsAndPages/EditScheduledEventsEditPage.tsx b/app/src/components/events/partials/ModalTabsAndPages/EditScheduledEventsEditPage.tsx index 54a4e7591c..b2fa2feb65 100644 --- a/app/src/components/events/partials/ModalTabsAndPages/EditScheduledEventsEditPage.tsx +++ b/app/src/components/events/partials/ModalTabsAndPages/EditScheduledEventsEditPage.tsx @@ -45,8 +45,8 @@ const EditScheduledEventsEditPage = ({ const seriesOptions = useAppSelector(state => getSchedulingSeriesOptions(state)); // TODO: Get rid of the wrappers when modernizing redux is done - const checkForSchedulingConflictsWrapper = (events: any) => { - dispatch(checkForSchedulingConflicts(events)); + const checkForSchedulingConflictsWrapper = async(events: any) => { + return dispatch(checkForSchedulingConflicts(events)); } const user = useAppSelector(state => getUserInformation(state)); diff --git a/app/src/components/events/partials/ModalTabsAndPages/EventDetailsWorkflowErrors.tsx b/app/src/components/events/partials/ModalTabsAndPages/EventDetailsWorkflowErrors.tsx index 279b8a3f6f..9ed26c9d72 100644 --- a/app/src/components/events/partials/ModalTabsAndPages/EventDetailsWorkflowErrors.tsx +++ b/app/src/components/events/partials/ModalTabsAndPages/EventDetailsWorkflowErrors.tsx @@ -42,7 +42,7 @@ const EventDetailsWorkflowErrors = ({ }; // @ts-expect-error TS(7006): Parameter 'tabType' implicitly has an 'any' type. - const openSubTab = (tabType, errorId: number | null = null) => { + const openSubTab = (tabType, errorId: number | undefined = undefined) => { dispatch(removeNotificationWizardForm()); setHierarchy(tabType); if (tabType === "workflow-error-details") { diff --git a/app/src/components/events/partials/ModalTabsAndPages/EventDetailsWorkflowOperations.tsx b/app/src/components/events/partials/ModalTabsAndPages/EventDetailsWorkflowOperations.tsx index 2f364b8e16..3a62d86176 100644 --- a/app/src/components/events/partials/ModalTabsAndPages/EventDetailsWorkflowOperations.tsx +++ b/app/src/components/events/partials/ModalTabsAndPages/EventDetailsWorkflowOperations.tsx @@ -42,7 +42,7 @@ const EventDetailsWorkflowOperations = ({ }, []); // @ts-expect-error TS(7006): Parameter 'tabType' implicitly has an 'any' type. - const openSubTab = (tabType, operationId: number | null = null) => { + const openSubTab = (tabType, operationId: number | undefined = undefined) => { dispatch(removeNotificationWizardForm()); setHierarchy(tabType); if (tabType === "workflow-operation-details") { diff --git a/app/src/components/events/partials/modals/EditMetadataEventsModal.tsx b/app/src/components/events/partials/modals/EditMetadataEventsModal.tsx index a39a7416b8..57531c2240 100644 --- a/app/src/components/events/partials/modals/EditMetadataEventsModal.tsx +++ b/app/src/components/events/partials/modals/EditMetadataEventsModal.tsx @@ -15,6 +15,7 @@ import RenderMultiField from "../../../shared/wizard/RenderMultiField"; import { getUserInformation } from "../../../../selectors/userInfoSelectors"; import { useAppDispatch, useAppSelector } from "../../../../store"; import { + MetadataFieldSelected, postEditMetadata, updateBulkMetadata, } from "../../../../slices/eventSlice"; @@ -36,7 +37,15 @@ const EditMetadataEventsModal = ({ const dispatch = useAppDispatch(); const [selectedEvents] = useState(selectedRows); - const [metadataFields, setMetadataFields] = useState({}); + const [metadataFields, setMetadataFields] = useState<{ + merged: string[], + mergedMetadata: MetadataFieldSelected[], + notFound?: string[], + runningWorkflow?: string[], + }>({ + merged: [], + mergedMetadata: [] + }); const [loading, setLoading] = useState(true); const [fatalError, setFatalError] = useState({}); const [fetchedValues, setFetchedValues] = useState(null); @@ -66,7 +75,12 @@ const EditMetadataEventsModal = ({ let initialValues = getInitialValues(result); // @ts-expect-error TS(2345): Argument of type '{}' is not assignable to paramet... Remove this comment to see the full error message setFetchedValues(initialValues); - setMetadataFields(result); + setMetadataFields({ + merged: result.merged, + mergedMetadata: result.mergedMetadata, + notFound: result.notFound, + runningWorkflow: result.runningWorkflow, + }); } }); setLoading(false); @@ -86,7 +100,6 @@ const EditMetadataEventsModal = ({ const onChangeSelected = (e, fieldId) => { let selected = e.target.checked; let fields = metadataFields; -// @ts-expect-error TS(2339): Property 'mergedMetadata' does not exist on type '... Remove this comment to see the full error message fields.mergedMetadata = metadataFields.mergedMetadata.map((field) => { if (field.id === fieldId) { return { @@ -111,7 +124,6 @@ const EditMetadataEventsModal = ({ // @ts-expect-error TS(2531): Object is possibly 'null'. if (fetchedValues[field.id] !== formikValues[field.id]) { let fields = metadataFields; -// @ts-expect-error TS(2339): Property 'mergedMetadata' does not exist on type '... Remove this comment to see the full error message fields.mergedMetadata = metadataFields.mergedMetadata.map((f) => { if (f.id === field.id) { return { @@ -221,9 +233,7 @@ const EditMetadataEventsModal = ({ -{/* @ts-expect-error TS(2339): Property 'mergedMetadata' does not exist on type '... Remove this comment to see the full error message */} {metadataFields.mergedMetadata.map( -// @ts-expect-error TS(7006): Parameter 'metadata' implicitly has an 'any' type. (metadata, key) => !metadata.readOnly && ( getRecordings(state)); // TODO: Get rid of the wrappers when modernizing redux is done - const checkForSchedulingConflictsWrapper = (events: any) => { - dispatch(checkForSchedulingConflicts(events)); + const checkForSchedulingConflictsWrapper = async(events: any) => { + return dispatch(checkForSchedulingConflicts(events)); } const initialValues = initialFormValuesEditScheduledEvents; diff --git a/app/src/components/events/partials/wizards/NewEventWizard.tsx b/app/src/components/events/partials/wizards/NewEventWizard.tsx index 830adc6665..4d4625b2c5 100644 --- a/app/src/components/events/partials/wizards/NewEventWizard.tsx +++ b/app/src/components/events/partials/wizards/NewEventWizard.tsx @@ -49,7 +49,7 @@ const NewEventWizard: React.FC<{ const [page, setPage] = useState(0); const [snapshot, setSnapshot] = useState(initialValues); - const [pageCompleted, setPageCompleted] = useState({}); + const [pageCompleted, setPageCompleted] = useState<{ [key: number]: boolean }>({}); // Caption of steps used by Stepper const steps = [ @@ -101,7 +101,6 @@ const NewEventWizard: React.FC<{ // set page as completely filled out let updatedPageCompleted = pageCompleted; -// @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message updatedPageCompleted[page] = true; setPageCompleted(updatedPageCompleted); diff --git a/app/src/slices/aclDetailsSlice.ts b/app/src/slices/aclDetailsSlice.ts index 321c3fec70..98772db8f8 100644 --- a/app/src/slices/aclDetailsSlice.ts +++ b/app/src/slices/aclDetailsSlice.ts @@ -6,13 +6,22 @@ import { addNotification } from '../slices/notificationSlice'; /** * This file contains redux reducer for actions affecting the state of details of an ACL */ -type IncomingAcl = { - ace: { allow: boolean, role: string, action: string }[] +export type Ace = { + allow: boolean, + role: string, + action: string } -type TransformedAcls = { +type IncomingAcls = { + ace: Ace[] +} + +export type TransformedAcl = { actions: string[], role: string, read: boolean, write: boolean -}[] +} + + +export type TransformedAcls = TransformedAcl[] type AclDetailsState = { status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', @@ -34,12 +43,12 @@ const initialState: AclDetailsState = { }; // fetch details about a certain acl from server -export const fetchAclDetails = createAsyncThunk('aclDetails/fetchAclDetails', async (aclId: any, {dispatch}) => { +export const fetchAclDetails = createAsyncThunk('aclDetails/fetchAclDetails', async (aclId: number) => { const res = await axios.get(`/admin-ng/acl/${aclId}`); let aclDetails = res.data; - let acl: IncomingAcl = aclDetails.acl; + let acl: IncomingAcls = aclDetails.acl; let transformedAcls: TransformedAcls = []; // transform policies for further use @@ -119,7 +128,13 @@ export const fetchAclDetails = createAsyncThunk('aclDetails/fetchAclDetails', as }); // update details of a certain acl -export const updateAclDetails = createAsyncThunk('aclDetails/updateAclDetails', async (params: {values: any, aclId: any}, {dispatch}) => { +export const updateAclDetails = createAsyncThunk('aclDetails/updateAclDetails', async (params: { + values: { + name: string, + acls: TransformedAcls, + }, + aclId: number, +}, {dispatch}) => { const { values, aclId } = params // transform ACLs back to structure used by backend let acls = prepareAccessPolicyRulesForPost(values.acls); diff --git a/app/src/slices/eventDetailsSlice.ts b/app/src/slices/eventDetailsSlice.ts index 665b86c43c..5547a472e7 100644 --- a/app/src/slices/eventDetailsSlice.ts +++ b/app/src/slices/eventDetailsSlice.ts @@ -28,6 +28,7 @@ import { getRecordings } from "../selectors/recordingSelectors"; import { Workflow as WorkflowDefinitions} from "../slices/workflowSlice"; import { RootState } from '../store'; import { Statistics, fetchStatistics, fetchStatisticsValueUpdate } from './statisticsSlice'; +import { Ace, TransformedAcl, TransformedAcls } from './aclDetailsSlice'; type MetadataField = { id: string, @@ -558,7 +559,7 @@ const initialState: EventDetailsState = { }; -export const fetchMetadata = createAsyncThunk('eventDetails/fetchMetadata', async (eventId: any) => { +export const fetchMetadata = createAsyncThunk('eventDetails/fetchMetadata', async (eventId: string) => { const metadataRequest = await axios.get(`/admin-ng/event/${eventId}/metadata.json`); const metadataResponse = await metadataRequest.data; @@ -604,7 +605,7 @@ export const fetchMetadata = createAsyncThunk('eventDetails/fetchMetadata', asyn return { metadata, extendedMetadata } }); -export const fetchAssets = createAsyncThunk('eventDetails/fetchAssets', async (eventId: any, { dispatch }) => { +export const fetchAssets = createAsyncThunk('eventDetails/fetchAssets', async (eventId: string, { dispatch }) => { const assetsRequest = await axios.get( `/admin-ng/event/${eventId}/asset/assets.json` ); @@ -685,7 +686,7 @@ const formatUploadAssetOptions = (optionsData: object) => { return optionsResult; }; -export const fetchAssetAttachments = createAsyncThunk('eventDetails/fetchAssetAttachments', async (eventId: any) => { +export const fetchAssetAttachments = createAsyncThunk('eventDetails/fetchAssetAttachments', async (eventId: string) => { let params = new URLSearchParams(); params.append("id1", "attachment"); @@ -696,7 +697,10 @@ export const fetchAssetAttachments = createAsyncThunk('eventDetails/fetchAssetAt return await attachmentsRequest.data; }); -export const fetchAssetAttachmentDetails = createAsyncThunk('eventDetails/fetchAssetAttachmentDetails', async (params: {eventId: any, attachmentId: any}) => { +export const fetchAssetAttachmentDetails = createAsyncThunk('eventDetails/fetchAssetAttachmentDetails', async (params: { + eventId: string, + attachmentId: string +}) => { const { eventId, attachmentId } = params; let searchParams = new URLSearchParams(); searchParams.append("id1", "attachment"); @@ -708,7 +712,7 @@ export const fetchAssetAttachmentDetails = createAsyncThunk('eventDetails/fetchA return await attachmentDetailsRequest.data; }); -export const fetchAssetCatalogs = createAsyncThunk('eventDetails/fetchAssetCatalogs', async (eventId: any) => { +export const fetchAssetCatalogs = createAsyncThunk('eventDetails/fetchAssetCatalogs', async (eventId: string) => { let params = new URLSearchParams(); params.append("id1", "catalog"); @@ -719,7 +723,10 @@ export const fetchAssetCatalogs = createAsyncThunk('eventDetails/fetchAssetCatal return await catalogsRequest.data; }); -export const fetchAssetCatalogDetails = createAsyncThunk('eventDetails/fetchAssetCatalogDetails', async (params: {eventId: any, catalogId: any}) => { +export const fetchAssetCatalogDetails = createAsyncThunk('eventDetails/fetchAssetCatalogDetails', async (params: { + eventId: string, + catalogId: string +}) => { const { eventId, catalogId } = params; let searchParams = new URLSearchParams(); searchParams.append("id1", "catalog"); @@ -731,7 +738,7 @@ export const fetchAssetCatalogDetails = createAsyncThunk('eventDetails/fetchAsse return await catalogDetailsRequest.data; }); -export const fetchAssetMedia = createAsyncThunk('eventDetails/fetchAssetMedia', async (eventId: any) => { +export const fetchAssetMedia = createAsyncThunk('eventDetails/fetchAssetMedia', async (eventId: string) => { let params = new URLSearchParams(); params.append("id1", "media"); @@ -756,7 +763,10 @@ export const fetchAssetMedia = createAsyncThunk('eventDetails/fetchAssetMedia', return media; }); -export const fetchAssetMediaDetails = createAsyncThunk('eventDetails/fetchAssetMediaDetails', async (params: {eventId: any, mediaId: any}) => { +export const fetchAssetMediaDetails = createAsyncThunk('eventDetails/fetchAssetMediaDetails', async (params: { + eventId: string, + mediaId: string +}) => { const { eventId, mediaId } = params; let searchParams = new URLSearchParams(); searchParams.append("id1", "media"); @@ -785,7 +795,7 @@ export const fetchAssetMediaDetails = createAsyncThunk('eventDetails/fetchAssetM return mediaDetails; }); -export const fetchAssetPublications = createAsyncThunk('eventDetails/fetchAssetPublications', async (eventId: any) => { +export const fetchAssetPublications = createAsyncThunk('eventDetails/fetchAssetPublications', async (eventId: string) => { let params = new URLSearchParams(); params.append("id1", "publication"); @@ -796,7 +806,10 @@ export const fetchAssetPublications = createAsyncThunk('eventDetails/fetchAssetP return await publicationsRequest.data; }); -export const fetchAssetPublicationDetails = createAsyncThunk('eventDetails/fetchAssetPublicationDetails', async (params: {eventId: any, publicationId: any}) => { +export const fetchAssetPublicationDetails = createAsyncThunk('eventDetails/fetchAssetPublicationDetails', async (params: { + eventId: string, + publicationId: string +}) => { const { eventId, publicationId } = params; let searchParams = new URLSearchParams(); searchParams.append("id1", "publication"); @@ -808,41 +821,36 @@ export const fetchAssetPublicationDetails = createAsyncThunk('eventDetails/fetch return await publicationDetailsRequest.data; }); -export const fetchAccessPolicies = createAsyncThunk('eventDetails/fetchAccessPolicies', async (eventId: any) => { +export const fetchAccessPolicies = createAsyncThunk('eventDetails/fetchAccessPolicies', async (eventId: string) => { const policyData = await axios.get( `/admin-ng/event/${eventId}/access.json` ); let accessPolicies = await policyData.data; - let policies: any[] = []; + let policies: TransformedAcls = []; if (!!accessPolicies.episode_access) { const json = JSON.parse(accessPolicies.episode_access.acl).acl.ace; - let newPolicies = {}; - let policyRoles = []; + let newPolicies: { [key: string]: TransformedAcl } = {}; + let policyRoles: string[] = []; for (let i = 0; i < json.length; i++) { - const policy = json[i]; -// @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message + const policy: Ace = json[i]; if (!newPolicies[policy.role]) { -// @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message newPolicies[policy.role] = createPolicy(policy.role); policyRoles.push(policy.role); } if (policy.action === "read" || policy.action === "write") { -// @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message newPolicies[policy.role][policy.action] = policy.allow; - } else if (policy.allow === true || policy.allow === "true") { -// @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message + } else if (policy.allow === true) { //|| policy.allow === "true") { newPolicies[policy.role].actions.push(policy.action); } } -// @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message policies = policyRoles.map((role) => newPolicies[role]); } return policies; }); -export const fetchComments = createAsyncThunk('eventDetails/fetchComments', async (eventId: any) => { +export const fetchComments = createAsyncThunk('eventDetails/fetchComments', async (eventId: string) => { const commentsData = await axios.get(`/admin-ng/event/${eventId}/comments`); const comments = await commentsData.data; @@ -854,7 +862,7 @@ export const fetchComments = createAsyncThunk('eventDetails/fetchComments', asyn return { comments, commentReasons } }); -export const fetchEventPublications = createAsyncThunk('eventDetails/fetchEventPublications', async (eventId: any, { dispatch }) => { +export const fetchEventPublications = createAsyncThunk('eventDetails/fetchEventPublications', async (eventId: string) => { let data = await axios.get(`/admin-ng/event/${eventId}/publications.json`); let publications = await data.data; @@ -899,7 +907,11 @@ export const fetchEventPublications = createAsyncThunk('eventDetails/fetchEventP return publications.publications; }); -export const saveComment = createAsyncThunk('eventDetails/saveComment', async (params: {eventId: any, commentText: any, commentReason: any}, { dispatch }) => { +export const saveComment = createAsyncThunk('eventDetails/saveComment', async (params: { + eventId: string, + commentText: string, + commentReason: string +}) => { const { eventId, commentText, commentReason } = params; let headers = getHttpHeaders(); @@ -917,13 +929,18 @@ export const saveComment = createAsyncThunk('eventDetails/saveComment', async (p return true; }); -export const saveCommentReply = createAsyncThunk('eventDetails/saveCommentReply', async (params: {eventId: any, commentId: any, replyText: any, commentResolved: any}) => { +export const saveCommentReply = createAsyncThunk('eventDetails/saveCommentReply', async (params: { + eventId: string, + commentId: string, + replyText: string, + commentResolved: boolean +}) => { const { eventId, commentId, replyText, commentResolved } = params; let headers = getHttpHeaders(); let data = new URLSearchParams(); data.append("text", replyText); - data.append("resolved", commentResolved); + data.append("resolved", String(commentResolved)); const commentReply = await axios.post( `/admin-ng/event/${eventId}/comment/${commentId}/reply`, @@ -936,7 +953,7 @@ export const saveCommentReply = createAsyncThunk('eventDetails/saveCommentReply' return true; }); -export const fetchSchedulingInfo = createAsyncThunk('eventDetails/fetchSchedulingInfo', async (eventId: any, { dispatch, getState }) => { +export const fetchSchedulingInfo = createAsyncThunk('eventDetails/fetchSchedulingInfo', async (eventId: string, { dispatch, getState }) => { // get data from API about event scheduling const schedulingRequest = await axios.get( `/admin-ng/event/${eventId}/scheduling.json` @@ -1014,7 +1031,23 @@ export const fetchSchedulingInfo = createAsyncThunk('eventDetails/fetchSchedulin return source; }); -export const saveSchedulingInfo = createAsyncThunk('eventDetails/saveSchedulingInfo', async (params: {eventId: any, values: any, startDate: any, endDate: any}, { dispatch, getState }) => { +export const saveSchedulingInfo = createAsyncThunk('eventDetails/saveSchedulingInfo', async (params: { + eventId: string, + values: { + captureAgent: string, + inputs: string[], + scheduleDurationHours: string, + scheduleDurationMinutes: string, + scheduleEndDate: string, + scheduleEndHour: string, + scheduleEndMinute: string, + scheduleStartDate: string, + scheduleStartHour: string, + scheduleStartMinute: string, + }, + startDate: Date, + endDate: Date +}, { dispatch, getState }) => { const { eventId, values, startDate, endDate } = params; const state = getState() as RootState; @@ -1044,12 +1077,12 @@ export const saveSchedulingInfo = createAsyncThunk('eventDetails/saveSchedulingI ...oldSource, agentId: device.id, start: { - date: startDate, + date: startDate.toISOString(), hour: parseInt(values.scheduleStartHour), minute: parseInt(values.scheduleStartMinute), }, end: { - date: endDate, + date: endDate.toISOString(), hour: parseInt(values.scheduleEndHour), minute: parseInt(values.scheduleEndMinute), }, @@ -1104,7 +1137,12 @@ export const saveSchedulingInfo = createAsyncThunk('eventDetails/saveSchedulingI // TODO: This does not return a boolean anymore. Fix this in usage, make users // get their info from the state -export const checkConflicts = createAsyncThunk('eventDetails/checkConflicts', async (params: {eventId: any, startDate: any, endDate: any, deviceId: any}, { dispatch }) => { +export const checkConflicts = createAsyncThunk('eventDetails/checkConflicts', async (params: { + eventId: string, + startDate: Date, + endDate: Date, + deviceId: string +}, { dispatch }) => { const { eventId, startDate, endDate, deviceId } = params; const conflicts: any[] = []; let hasSchedulingConflicts = false; @@ -1129,7 +1167,7 @@ if (endDate < now) { const conflictTimeFrame = { id: eventId, start: startDate.toISOString(), - duration: endDate - startDate, + duration: endDate.getTime() - startDate.getTime(), device: deviceId, end: endDate.toISOString(), }; @@ -1203,7 +1241,7 @@ if (endDate < now) { return { conflicts, hasSchedulingConflicts }; }); -export const fetchWorkflows = createAsyncThunk('eventDetails/fetchWorkflows', async (eventId: any, { dispatch, getState }) => { +export const fetchWorkflows = createAsyncThunk('eventDetails/fetchWorkflows', async (eventId: string, { dispatch, getState }) => { // todo: show notification if there are active transactions // dispatch(addNotification('warning', 'ACTIVE_TRANSACTION', -1, null, NOTIFICATION_CONTEXT)); @@ -1245,7 +1283,10 @@ export const fetchWorkflows = createAsyncThunk('eventDetails/fetchWorkflows', as return workflows; }); -export const fetchWorkflowDetails = createAsyncThunk('eventDetails/fetchWorkflowDetails', async (params: {eventId: any, workflowId: any}, { getState }) => { +export const fetchWorkflowDetails = createAsyncThunk('eventDetails/fetchWorkflowDetails', async (params: { + eventId: string, + workflowId: string +}) => { const { eventId, workflowId } = params; const data = await axios.get( `/admin-ng/event/${eventId}/workflows/${workflowId}.json` @@ -1253,7 +1294,12 @@ export const fetchWorkflowDetails = createAsyncThunk('eventDetails/fetchWorkflow return await data.data; }); -export const performWorkflowAction = createAsyncThunk('eventDetails/performWorkflowAction', async (params: {eventId: any, workflowId: any, action: any, close: any}, { dispatch }) => { +export const performWorkflowAction = createAsyncThunk('eventDetails/performWorkflowAction', async (params: { + eventId: string, + workflowId: string, + action: string, + close: () => void, +}, { dispatch }) => { const { eventId, workflowId, action, close} = params; let headers = { headers: { @@ -1299,7 +1345,10 @@ export const performWorkflowAction = createAsyncThunk('eventDetails/performWorkf }); }); -export const deleteWorkflow = createAsyncThunk('eventDetails/deleteWorkflow', async (params: {eventId: any, workflowId: any}, { dispatch, getState }) => { +export const deleteWorkflow = createAsyncThunk('eventDetails/deleteWorkflow', async (params: { + eventId: string, + workflowId: number +}, { dispatch, getState }) => { const { eventId, workflowId } = params; const workflowEntries = await axios @@ -1340,7 +1389,10 @@ export const deleteWorkflow = createAsyncThunk('eventDetails/deleteWorkflow', as return workflowEntries; }); -export const fetchWorkflowOperations = createAsyncThunk('eventDetails/fetchWorkflowOperations', async (params: {eventId: any, workflowId: any}) => { +export const fetchWorkflowOperations = createAsyncThunk('eventDetails/fetchWorkflowOperations', async (params: { + eventId: string, + workflowId: string +}) => { const { eventId, workflowId } = params; const data = await axios.get( `/admin-ng/event/${eventId}/workflows/${workflowId}/operations.json` @@ -1349,7 +1401,11 @@ export const fetchWorkflowOperations = createAsyncThunk('eventDetails/fetchWorkf return { entries: workflowOperationsData }; }); -export const fetchWorkflowOperationDetails = createAsyncThunk('eventDetails/fetchWorkflowOperationDetails', async (params: {eventId: any, workflowId: any, operationId: any}) => { +export const fetchWorkflowOperationDetails = createAsyncThunk('eventDetails/fetchWorkflowOperationDetails', async (params: { + eventId: string, + workflowId: string, + operationId?: number +}) => { const { eventId, workflowId, operationId } = params; const data = await axios.get( `/admin-ng/event/${eventId}/workflows/${workflowId}/operations/${operationId}` @@ -1357,7 +1413,10 @@ export const fetchWorkflowOperationDetails = createAsyncThunk('eventDetails/fetc return await data.data; }); -export const fetchWorkflowErrors = createAsyncThunk('eventDetails/fetchWorkflowErrors', async (params: {eventId: any, workflowId: any}) => { +export const fetchWorkflowErrors = createAsyncThunk('eventDetails/fetchWorkflowErrors', async (params: { + eventId: string, + workflowId: string +}) => { const { eventId, workflowId } = params; const data = await axios.get( `/admin-ng/event/${eventId}/workflows/${workflowId}/errors.json` @@ -1366,7 +1425,11 @@ export const fetchWorkflowErrors = createAsyncThunk('eventDetails/fetchWorkflowE return { entries: workflowErrorsData }; }); -export const fetchWorkflowErrorDetails = createAsyncThunk('eventDetails/fetchWorkflowErrorDetails', async (params: {eventId: any, workflowId: any, errorId: any}) => { +export const fetchWorkflowErrorDetails = createAsyncThunk('eventDetails/fetchWorkflowErrorDetails', async (params: { + eventId: string, + workflowId: string, + errorId?: number +}) => { const { eventId, workflowId, errorId } = params; const data = await axios.get( `/admin-ng/event/${eventId}/workflows/${workflowId}/errors/${errorId}.json` @@ -1375,7 +1438,7 @@ export const fetchWorkflowErrorDetails = createAsyncThunk('eventDetails/fetchWor }); // TODO: Fix this after the modernization of statisticsThunks happened -export const fetchEventStatistics = createAsyncThunk('eventDetails/fetchEventStatistics', async (eventId: any, { getState }) => { +export const fetchEventStatistics = createAsyncThunk('eventDetails/fetchEventStatistics', async (eventId: string, { getState }) => { // get prior statistics const state = getState(); const statistics = getStatistics(state as RootState); @@ -1390,7 +1453,14 @@ export const fetchEventStatistics = createAsyncThunk('eventDetails/fetchEventSta }); // TODO: Fix this after the modernization of statisticsThunks happened -export const fetchEventStatisticsValueUpdate = createAsyncThunk('eventDetails/fetchEventStatisticsValueUpdate', async (params: {eventId: any, providerId: any, from: any, to: any, dataResolution: any, timeMode: any}, { getState }) => { +export const fetchEventStatisticsValueUpdate = createAsyncThunk('eventDetails/fetchEventStatisticsValueUpdate', async (params: { + eventId: string, + providerId: string, + from: string, + to: string, + dataResolution: string[], + timeMode: any +}, { getState }) => { const { eventId, providerId, from, to, dataResolution, timeMode } = params; // get prior statistics const state = getState(); @@ -1410,7 +1480,10 @@ export const fetchEventStatisticsValueUpdate = createAsyncThunk('eventDetails/fe ); }); -export const updateMetadata = createAsyncThunk('eventDetails/updateMetadata', async (params: {eventId: any, values: any}, { dispatch, getState }) => { +export const updateMetadata = createAsyncThunk('eventDetails/updateMetadata', async (params: { + eventId: string, + values: { [key: string]: any } +}, { dispatch, getState }) => { const { eventId, values } = params; let metadataInfos = getMetadata(getState() as RootState); @@ -1431,7 +1504,15 @@ export const updateMetadata = createAsyncThunk('eventDetails/updateMetadata', as dispatch(setEventMetadata(eventMetadata)); }); -export const updateExtendedMetadata = createAsyncThunk('eventDetails/updateExtendedMetadata', async (params: {eventId: any, values: any, catalog: any}, { dispatch, getState }) => { +export const updateExtendedMetadata = createAsyncThunk('eventDetails/updateExtendedMetadata', async (params: { + eventId: string, + values: { [key: string]: any }, + catalog: { + flavor: string, + title: string, + fields: { [key: string]: any }[] + } +}, { dispatch, getState }) => { const { eventId, values, catalog } = params; const { fields, data, headers } = transformMetadataForUpdate( @@ -1464,7 +1545,7 @@ export const updateExtendedMetadata = createAsyncThunk('eventDetails/updateExten dispatch(setExtendedEventMetadata(newExtendedMetadata)); }); -export const fetchHasActiveTransactions = createAsyncThunk('eventDetails/fetchHasActiveTransactions', async (eventId: any) => { +export const fetchHasActiveTransactions = createAsyncThunk('eventDetails/fetchHasActiveTransactions', async (eventId: string) => { const transactionsData = await axios.get( `/admin-ng/event/${eventId}/hasActiveTransaction` ); @@ -1472,7 +1553,10 @@ export const fetchHasActiveTransactions = createAsyncThunk('eventDetails/fetchHa return hasActiveTransactions; }); -export const updateAssets = createAsyncThunk('eventDetails/updateAssets', async (params: {values: any, eventId: any}, { dispatch, getState }) => { +export const updateAssets = createAsyncThunk('eventDetails/updateAssets', async (params: { + values: { [key: string]: string }, + eventId: string +}, { dispatch, getState }) => { const { values, eventId } = params; // get asset upload options from redux store const state = getState() as RootState; @@ -1530,7 +1614,10 @@ export const updateAssets = createAsyncThunk('eventDetails/updateAssets', async }); }); -export const saveAccessPolicies = createAsyncThunk('eventDetails/saveAccessPolicies', async (params: {eventId: any, policies: any}, { dispatch }) => { +export const saveAccessPolicies = createAsyncThunk('eventDetails/saveAccessPolicies', async (params: { + eventId: string, + policies: { [key: string]: TransformedAcl } +}, { dispatch }) => { const { eventId, policies } = params; const headers = getHttpHeaders(); @@ -1569,7 +1656,10 @@ export const saveAccessPolicies = createAsyncThunk('eventDetails/saveAccessPolic }); }); -export const deleteComment = createAsyncThunk('eventDetails/deleteComment', async (params: {eventId: any, commentId: any}) => { +export const deleteComment = createAsyncThunk('eventDetails/deleteComment', async (params: { + eventId: string, + commentId: number +}) => { const { eventId, commentId } = params; const commentDeleted = await axios.delete( `/admin-ng/event/${eventId}/comment/${commentId}` @@ -1578,7 +1668,11 @@ export const deleteComment = createAsyncThunk('eventDetails/deleteComment', asyn return true; }); -export const deleteCommentReply = createAsyncThunk('eventDetails/deleteCommentReply', async (params: {eventId: any, commentId: any, replyId: any}) => { +export const deleteCommentReply = createAsyncThunk('eventDetails/deleteCommentReply', async (params: { + eventId: string, + commentId: number, + replyId: number +}) => { const { eventId, commentId, replyId } = params; const commentReplyDeleted = await axios.delete( `/admin-ng/event/${eventId}/comment/${commentId}/${replyId}` @@ -1588,7 +1682,7 @@ export const deleteCommentReply = createAsyncThunk('eventDetails/deleteCommentRe return true; }); -export const updateWorkflow = createAsyncThunk('eventDetails/updateWorkflow', async (workflowId: any, { dispatch, getState }) => { +export const updateWorkflow = createAsyncThunk('eventDetails/updateWorkflow', async (workflowId: string, { dispatch, getState }) => { const state = getState(); const workflowDefinitions = getWorkflowDefinitions(state as RootState); const workflowDef = workflowDefinitions.find((def) => def.id === workflowId); @@ -1601,7 +1695,13 @@ export const updateWorkflow = createAsyncThunk('eventDetails/updateWorkflow', as ); }); -export const saveWorkflowConfig = createAsyncThunk('eventDetails/saveWorkflowConfig', async (params: {values: any, eventId: any}, { dispatch }) => { +export const saveWorkflowConfig = createAsyncThunk('eventDetails/saveWorkflowConfig', async (params: { + values: { + workflowDefinition: string, + configuration: { [key: string]: any } + }, + eventId: string +}, { dispatch }) => { const { values, eventId } = params; let jsonData = { id: values.workflowDefinition, diff --git a/app/src/slices/eventSlice.ts b/app/src/slices/eventSlice.ts index ea47354054..2e8ad60cfc 100644 --- a/app/src/slices/eventSlice.ts +++ b/app/src/slices/eventSlice.ts @@ -1,6 +1,6 @@ import { PayloadAction, SerializedError, createAsyncThunk, createSlice } from '@reduxjs/toolkit' import { eventsTableConfig } from "../configs/tableConfigs/eventsTableConfig"; -import axios from 'axios'; +import axios, { AxiosProgressEvent } from 'axios'; import moment from "moment-timezone"; import { getURLParams, @@ -22,8 +22,9 @@ import { } from "../slices/notificationSlice"; import { getAssetUploadOptions, getSchedulingEditedEvents } from '../selectors/eventSelectors'; import { fetchSeriesOptions } from "../slices/seriesSlice"; -import { RootState } from '../store'; +import { AppDispatch, RootState } from '../store'; import { fetchAssetUploadOptions } from '../thunks/assetsThunks'; +import { TransformedAcls } from './aclDetailsSlice'; /** * This file contains redux reducer for actions affecting the state of events @@ -76,6 +77,7 @@ type Event = { name: string, url: string, }[], + selected?: boolean, series?: { id: string, title: string } source: string, start_date: string, @@ -86,19 +88,24 @@ type Event = { workflow_state: string, } +type MetadataField = { + differentValues?: boolean, + collection?: {}[], // different for e.g. languages and presenters + id: string, + label: string, + readOnly: boolean, + required: boolean, + translatable?: boolean, + type: string, + value: string | string[], +} + +export type MetadataFieldSelected = MetadataField & { selected: boolean } + type MetadataCatalog = { title: string, flavor: string, - fields: { - collection?: {}[], // different for e.g. languages and presenters - id: string, - label: string, - readOnly: boolean, - required: boolean, - translatable?: boolean, - type: string, - value: string | string[], - }[] + fields: MetadataField[], } type EditedEvents = { @@ -254,7 +261,7 @@ export const fetchEventMetadata = createAsyncThunk('events/fetchEventMetadata', }); // get merged metadata for provided event ids -export const postEditMetadata = createAsyncThunk('events/postEditMetadata', async (ids: any) => { +export const postEditMetadata = createAsyncThunk('events/postEditMetadata', async (ids: string[]) => { let formData = new URLSearchParams(); formData.append("eventIds", JSON.stringify(ids)); @@ -280,14 +287,26 @@ export const postEditMetadata = createAsyncThunk('events/postEditMetadata', asyn }; } catch (e) { // return error + if (e instanceof Error) { + return { + fatalError: e.message, + }; + } return { -// @ts-expect-error TS(2571): Object is of type 'unknown'. - fatalError: e.message, + fatalError: "", }; } }); -export const updateBulkMetadata = createAsyncThunk('events/updateBulkMetadata', async (params: {metadataFields: any, values: any}, { dispatch }) => { +export const updateBulkMetadata = createAsyncThunk('events/updateBulkMetadata', async (params: { + metadataFields: { + merged: string[], + mergedMetadata: MetadataFieldSelected[], + notFound?: string[], + runningWorkflow?: string[], + }, + values: { [key: string]: any} +}, { dispatch }) => { const { metadataFields, values } = params; let formData = new URLSearchParams(); @@ -300,7 +319,6 @@ export const updateBulkMetadata = createAsyncThunk('events/updateBulkMetadata', }, ]; -// @ts-expect-error TS(7006): Parameter 'field' implicitly has an 'any' type. metadataFields.mergedMetadata.forEach((field) => { if (field.selected) { let value = values[field.id]; @@ -366,7 +384,39 @@ export const updateBulkMetadata = createAsyncThunk('events/updateBulkMetadata', }); }); -export const postNewEvent = createAsyncThunk('events/postNewEvent', async (params: {values: any, metadataInfo: any, extendedMetadata: any}, { dispatch, getState }) => { +export const postNewEvent = createAsyncThunk('events/postNewEvent', async (params: { + values: { + acls: TransformedAcls, + configuration: { [key: string]: any }, + deviceInputs?: string[], + processingWorkflow: string, + repeatOn: string[], + scheduleDurationHours: string, + scheduleDurationMinutes: string, + scheduleEndDate: string, + scheduleEndHour: string, + scheduleEndMinute: string, + scheduleStartDate: string, + scheduleStartHour: string, + scheduleStartMinute: string, + sourceMode: string, + uploadAssetsTrack: { + accept: string, + displayOrder: number, + file: FileList, + flavorSubtType: string, + flavorType: string, + id: string, + multiple: boolean, + showAs: string, + title: string, + type: string, + }[], + [key: string]: unknown, + }, + metadataInfo: MetadataCatalog, + extendedMetadata: MetadataCatalog[], +}, { dispatch, getState }) => { const { values, metadataInfo, extendedMetadata } = params; // get asset upload options from redux store @@ -375,7 +425,6 @@ export const postNewEvent = createAsyncThunk('events/postNewEvent', async (param let formData = new FormData(); let metadataFields, extendedMetadataFields, metadata, source, access, assets; - let configuration = {}; // prepare metadata provided by user metadataFields = prepareMetadataFieldsForPost(metadataInfo.fields, values); @@ -422,8 +471,8 @@ export const postNewEvent = createAsyncThunk('events/postNewEvent', async (param // NOTE: if time zone issues still occur during further testing, try to set times to UTC (-offset) //startDate.setHours((values.scheduleStartHour - offset), values.scheduleStartMinute, 0, 0); startDate.setHours( - values.scheduleStartHour, - values.scheduleStartMinute, + parseInt(values.scheduleStartHour), + parseInt(values.scheduleStartMinute), 0, 0 ); @@ -438,12 +487,12 @@ export const postNewEvent = createAsyncThunk('events/postNewEvent', async (param } // NOTE: if time zone issues still occur during further testing, try to set times to UTC (-offset) //endDate.setHours((values.scheduleEndHour - offset), values.scheduleEndMinute, 0, 0); - endDate.setHours(values.scheduleEndHour, values.scheduleEndMinute, 0, 0); + endDate.setHours(parseInt(values.scheduleEndHour), parseInt(values.scheduleEndMinute), 0, 0); // transform duration into milliseconds let duration = - values.scheduleDurationHours * 3600000 + - values.scheduleDurationMinutes * 60000; + parseInt(values.scheduleDurationHours) * 3600000 + + parseInt(values.scheduleDurationMinutes) * 60000; // data about source for post request source = { @@ -490,10 +539,9 @@ export const postNewEvent = createAsyncThunk('events/postNewEvent', async (param values.sourceMode === "UPLOAD" ) { let asset = values.uploadAssetsTrack.find( -// @ts-expect-error TS(7006): Parameter 'asset' implicitly has an 'any' type. (asset) => asset.id === uploadAssetOptions[i].id ); - if (!!asset.file) { + if (!!asset && !!asset.file) { if (asset.multiple) { for (let j = 0; asset.file.length > j; j++) { formData.append(asset.id + "." + j, asset.file[j]); @@ -510,7 +558,7 @@ export const postNewEvent = createAsyncThunk('events/postNewEvent', async (param ) { formData.append( uploadAssetOptions[i].id + ".0", - values[uploadAssetOptions[i].id] + String(values[uploadAssetOptions[i].id]) ); assets.options = assets.options.concat(uploadAssetOptions[i]); } @@ -521,9 +569,9 @@ export const postNewEvent = createAsyncThunk('events/postNewEvent', async (param access = prepareAccessPolicyRulesForPost(values.acls); // prepare configurations for post + let configurationPrepared: { [key: string]: string } = {}; Object.keys(values.configuration).forEach((config) => { -// @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message - configuration[config] = String(values.configuration[config]); + configurationPrepared[config] = String(values.configuration[config]); }); for (const entry of extendedMetadataFields) { @@ -536,7 +584,7 @@ export const postNewEvent = createAsyncThunk('events/postNewEvent', async (param metadata: metadata, processing: { workflow: values.processingWorkflow, - configuration: configuration, + configuration: configurationPrepared, }, access: access, source: source, @@ -546,17 +594,18 @@ export const postNewEvent = createAsyncThunk('events/postNewEvent', async (param // Process bar notification var config = { - // @ts-expect-error TS(7006): Parameter 'id' implicitly has an 'any' type. - onUploadProgress: function(progressEvent) { - var percentCompleted = (progressEvent.loaded * 100) / progressEvent.total; - dispatch(addNotification({ - id: -42000, - type: "success", - key: "EVENTS_UPLOAD_STARTED", - duration: -1, - parameter: { "progress": percentCompleted.toFixed(2) } - })) - if (percentCompleted >= 100) { + onUploadProgress: function(progressEvent: AxiosProgressEvent) { + var percentCompleted = progressEvent.total ? (progressEvent.loaded * 100) / progressEvent.total : undefined; + if (percentCompleted) { + dispatch(addNotification({ + id: -42000, + type: "success", + key: "EVENTS_UPLOAD_STARTED", + duration: -1, + parameter: { "progress": percentCompleted.toFixed(2) } + })) + } + if (!percentCompleted || percentCompleted >= 100) { dispatch(removeNotification(-42000)) } }, @@ -578,7 +627,7 @@ export const postNewEvent = createAsyncThunk('events/postNewEvent', async (param }); // delete event with provided id -export const deleteEvent = createAsyncThunk('events/deleteEvent', async (id: any, { dispatch }) => { +export const deleteEvent = createAsyncThunk('events/deleteEvent', async (id: string, { dispatch }) => { // API call for deleting an event axios .delete(`/admin-ng/event/${id}`) @@ -600,7 +649,7 @@ export const deleteEvent = createAsyncThunk('events/deleteEvent', async (id: any }); }); -export const deleteMultipleEvent = createAsyncThunk('events/deleteMultipleEvent', async (events: any, { dispatch }) => { +export const deleteMultipleEvent = createAsyncThunk('events/deleteMultipleEvent', async (events: Event[], { dispatch }) => { let data = []; for (let i = 0; i < events.length; i++) { @@ -623,7 +672,11 @@ export const deleteMultipleEvent = createAsyncThunk('events/deleteMultipleEvent' }); }); -export const fetchScheduling = createAsyncThunk('events/fetchScheduling', async (params: {events: any, fetchNewScheduling: any, setFormikValue: any}, { dispatch, getState }) => { +export const fetchScheduling = createAsyncThunk('events/fetchScheduling', async (params: { + events: Event[], + fetchNewScheduling: boolean, + setFormikValue: any +}, { getState }) => { const { events, fetchNewScheduling, setFormikValue } = params; let editedEvents = []; @@ -695,18 +748,23 @@ export const fetchScheduling = createAsyncThunk('events/fetchScheduling', async }); // update multiple scheduled events at once -export const updateScheduledEventsBulk = createAsyncThunk('events/updateScheduledEventsBulk', async (values: any, { dispatch }) => { +export const updateScheduledEventsBulk = createAsyncThunk('events/updateScheduledEventsBulk', async ( + values: { + changedEvent: number, + changedEvents: string[], + editedEvents: EditedEvents[], + events: Event[], + }, +{ dispatch }) => { let formData = new FormData(); let update = []; let timezone = moment.tz.guess(); for (let i = 0; i < values.changedEvents.length; i++) { let eventChanges = values.editedEvents.find( -// @ts-expect-error TS(7006): Parameter 'event' implicitly has an 'any' type. (event) => event.eventId === values.changedEvents[i] ); let originalEvent = values.events.find( -// @ts-expect-error TS(7006): Parameter 'event' implicitly has an 'any' type. (event) => event.id === values.changedEvents[i] ); @@ -725,7 +783,7 @@ export const updateScheduledEventsBulk = createAsyncThunk('events/updateSchedule update.push({ events: [eventChanges.eventId], metadata: { - flavor: originalEvent.flavor, + // flavor: originalEvent.flavor, title: "EVENTS.EVENTS.DETAILS.CATALOG.EPISODE", fields: [ { @@ -786,8 +844,37 @@ export const updateScheduledEventsBulk = createAsyncThunk('events/updateSchedule }); // check provided date range for conflicts -// @ts-expect-error TS(7006): Parameter 'values' implicitly has an 'any' type. -export const checkConflicts = (values) => async (dispatch) => { + +export const checkConflicts = (values: { + acls: TransformedAcls, + configuration: { [key: string]: any }, + deviceInputs?: string[], + location: string, + processingWorkflow: string, + repeatOn: string[], + scheduleDurationHours: string, + scheduleDurationMinutes: string, + scheduleEndDate: string, + scheduleEndHour: string, + scheduleEndMinute: string, + scheduleStartDate: string, + scheduleStartHour: string, + scheduleStartMinute: string, + sourceMode: string, + uploadAssetsTrack: { + accept: string, + displayOrder: number, + file: FileList, + flavorSubtType: string, + flavorType: string, + id: string, + multiple: boolean, + showAs: string, + title: string, + type: string, + }[], + [key: string]: unknown, +}) => async (dispatch: AppDispatch) => { let check = true; // Only perform checks if source mode is SCHEDULE_SINGLE or SCHEDULE_MULTIPLE @@ -802,8 +889,8 @@ export const checkConflicts = (values) => async (dispatch) => { let startDate = new Date(values.scheduleStartDate); // NOTE: if time zone issues still occur during further testing, try to set times to UTC (-offset) startDate.setHours( - values.scheduleStartHour, - values.scheduleStartMinute, + parseInt(values.scheduleStartHour), + parseInt(values.scheduleStartMinute), 0, 0 ); @@ -824,7 +911,7 @@ export const checkConflicts = (values) => async (dispatch) => { const endDate = new Date(values.scheduleEndDate); // NOTE: if time zone issues still occur during further testing, try to set times to UTC (-offset) - endDate.setHours(values.scheduleEndHour, values.scheduleEndMinute, 0, 0); + endDate.setHours(parseInt(values.scheduleEndHour), parseInt(values.scheduleEndMinute), 0, 0); // if start date is higher than end date --> end date is before start date if (startDate > endDate) { @@ -842,8 +929,8 @@ export const checkConflicts = (values) => async (dispatch) => { // transform duration into milliseconds (needed for API request) let duration = - values.scheduleDurationHours * 3600000 + - values.scheduleDurationMinutes * 60000; + parseInt(values.scheduleDurationHours) * 3600000 + + parseInt(values.scheduleDurationMinutes) * 60000; // Check for conflicts with other already scheduled events let conflicts = @@ -876,15 +963,11 @@ export const checkConflicts = (values) => async (dispatch) => { // Check for conflicts with already scheduled events export const checkForConflicts = async ( -// @ts-expect-error TS(7006): Parameter 'startDate' implicitly has an 'any' type... Remove this comment to see the full error message - startDate, -// @ts-expect-error TS(7006): Parameter 'endDate' implicitly has an 'any' type. - endDate, -// @ts-expect-error TS(7006): Parameter 'duration' implicitly has an 'any' type. - duration, -// @ts-expect-error TS(7006): Parameter 'device' implicitly has an 'any' type. - device, - repeatOn = null + startDate: Date, + endDate: Date, + duration: number, + device: string, + repeatOn: string[] | undefined = undefined ) => { let metadata = !!repeatOn ? { @@ -892,7 +975,6 @@ export const checkForConflicts = async ( device: device, duration: duration.toString(), end: endDate, -// @ts-expect-error TS(2339): Property 'join' does not exist on type 'never'. rrule: `FREQ=WEEKLY;BYDAY=${repeatOn.join()};BYHOUR=${startDate.getHours()};BYMINUTE=${startDate.getMinutes()}`, } : { @@ -923,8 +1005,7 @@ export const checkForConflicts = async ( }; // check if there are any scheduling conflicts with other events -// @ts-expect-error TS(7006): Parameter 'events' implicitly has an 'any' type. -export const checkForSchedulingConflicts = (events) => async (dispatch) => { +export const checkForSchedulingConflicts = (events: EditedEvents[]) => async (dispatch: AppDispatch) => { const formData = new FormData(); let update = []; let timezone = moment.tz.guess(); diff --git a/app/src/slices/groupDetailsSlice.ts b/app/src/slices/groupDetailsSlice.ts index 8caf307c1d..217470184e 100644 --- a/app/src/slices/groupDetailsSlice.ts +++ b/app/src/slices/groupDetailsSlice.ts @@ -30,7 +30,7 @@ const initialState: GroupDetailsState = { }; // fetch details about certain group from server -export const fetchGroupDetails = createAsyncThunk('groupDetails/fetchGroupDetails', async (groupName: any, {dispatch}) => { +export const fetchGroupDetails = createAsyncThunk('groupDetails/fetchGroupDetails', async (groupName: string) => { const res = await axios.get(`/admin-ng/groups/${groupName}`); const response = await res.data; @@ -58,7 +58,10 @@ export const fetchGroupDetails = createAsyncThunk('groupDetails/fetchGroupDetail }); // update details of a certain group -export const updateGroupDetails = createAsyncThunk('groupDetails/updateGroupDetails', async (params: {values: any, groupId: any}, {dispatch}) => { +export const updateGroupDetails = createAsyncThunk('groupDetails/updateGroupDetails', async (params: { + values: GroupDetailsState, + groupId: string +}, {dispatch}) => { const { values, groupId } = params // get URL params used for put request diff --git a/app/src/slices/groupSlice.ts b/app/src/slices/groupSlice.ts index 91752ae174..4ca595da05 100644 --- a/app/src/slices/groupSlice.ts +++ b/app/src/slices/groupSlice.ts @@ -3,6 +3,7 @@ import { groupsTableConfig } from '../configs/tableConfigs/groupsTableConfig'; import axios from 'axios'; import { buildGroupBody, getURLParams } from '../utils/resourceUtils'; import { addNotification } from '../slices/notificationSlice'; +import { TableConfig } from '../configs/tableConfigs/aclsTableConfig'; /** * This file contains redux reducer for actions affecting the state of groups @@ -19,7 +20,7 @@ type GroupState = { status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', error: SerializedError | null, results: Group[], - columns: any, // TODO: proper typing, derive from `initialColumns` + columns: TableConfig["columns"], total: number, count: number, offset: number, @@ -56,7 +57,7 @@ export const fetchGroups = createAsyncThunk('groups/fetchGroups', async (_, { ge }); // post new group to backend -export const postNewGroup = createAsyncThunk('groups/postNewGroup', async (values: any, {dispatch}) => { +export const postNewGroup = createAsyncThunk('groups/postNewGroup', async (values: Group, {dispatch}) => { // get URL params used for post request let data = buildGroupBody(values); @@ -81,7 +82,7 @@ export const postNewGroup = createAsyncThunk('groups/postNewGroup', async (value }); }); -export const deleteGroup = createAsyncThunk('groups/deleteGroup', async (id: any, {dispatch}) => { +export const deleteGroup = createAsyncThunk('groups/deleteGroup', async (id: string, {dispatch}) => { // API call for deleting a group axios .delete(`/admin-ng/groups/${id}`) diff --git a/app/src/slices/jobSlice.ts b/app/src/slices/jobSlice.ts index 92bdb6a546..1fa11a5976 100644 --- a/app/src/slices/jobSlice.ts +++ b/app/src/slices/jobSlice.ts @@ -2,6 +2,7 @@ import { PayloadAction, SerializedError, createAsyncThunk, createSlice } from '@ import { jobsTableConfig } from '../configs/tableConfigs/jobsTableConfig'; import axios from 'axios'; import { getURLParams } from '../utils/resourceUtils'; +import { TableConfig } from '../configs/tableConfigs/aclsTableConfig'; /** * This file contains redux reducer for actions affecting the state of jobs @@ -22,7 +23,7 @@ type JobState = { status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', error: SerializedError | null, results: Job[], - columns: any, // TODO: proper typing, derive from `initialColumns` + columns: TableConfig["columns"], total: number, count: number, offset: number, diff --git a/app/src/slices/notificationSlice.ts b/app/src/slices/notificationSlice.ts index 945a4586fb..c25ebf8467 100644 --- a/app/src/slices/notificationSlice.ts +++ b/app/src/slices/notificationSlice.ts @@ -42,7 +42,14 @@ const initialState: NotificationState = { // Counter for id of notifications let nextNotificationId = 0; -export const addNotification = createAsyncThunk('notifications/addNotification', async (params: {type: OurNotification["type"], key: OurNotification["key"], duration?: OurNotification["duration"], parameter?: OurNotification["parameter"], context?: OurNotification["context"], id?: OurNotification["id"]}, {dispatch, getState}) => { +export const addNotification = createAsyncThunk('notifications/addNotification', async (params: { + type: OurNotification["type"], + key: OurNotification["key"], + duration?: OurNotification["duration"], + parameter?: OurNotification["parameter"], + context?: OurNotification["context"], + id?: OurNotification["id"] +}, {dispatch, getState}) => { let { type, key, duration, parameter, context, id } = params if (!duration) { // fall back to defaults diff --git a/app/src/slices/recordingDetailsSlice.ts b/app/src/slices/recordingDetailsSlice.ts index e7cff612be..c83c8711b9 100644 --- a/app/src/slices/recordingDetailsSlice.ts +++ b/app/src/slices/recordingDetailsSlice.ts @@ -33,7 +33,7 @@ const initialState: RecordingDetailsState = { }; // fetch details of certain recording from server -export const fetchRecordingDetails = createAsyncThunk('recordingDetails/fetchRecordingDetails', async (name: any, { getState }) => { +export const fetchRecordingDetails = createAsyncThunk('recordingDetails/fetchRecordingDetails', async (name: string) => { // 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. diff --git a/app/src/slices/recordingSlice.ts b/app/src/slices/recordingSlice.ts index 961ea59f4c..eb3398ab43 100644 --- a/app/src/slices/recordingSlice.ts +++ b/app/src/slices/recordingSlice.ts @@ -3,6 +3,7 @@ import { recordingsTableConfig } from '../configs/tableConfigs/recordingsTableCo import axios from 'axios'; import { getURLParams } from '../utils/resourceUtils'; import { addNotification } from '../slices/notificationSlice'; +import { TableConfig } from '../configs/tableConfigs/aclsTableConfig'; /** * This file contains redux reducer for actions affecting the state of recordings @@ -23,7 +24,7 @@ type RecordingState = { status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', error: SerializedError | null, results: Recording[], - columns: any, // TODO: proper typing, derive from `initialColumns` + columns: TableConfig["columns"], total: number, count: number, offset: number, @@ -49,7 +50,7 @@ const initialState: RecordingState = { }; // fetch recordings from server -export const fetchRecordings = createAsyncThunk('recordings/fetchRecordings', async (flag: any, { getState }) => { +export const fetchRecordings = createAsyncThunk('recordings/fetchRecordings', async (flag: string | undefined, { getState }) => { let res; if (flag === "inputs") { @@ -92,7 +93,7 @@ export const fetchRecordings = createAsyncThunk('recordings/fetchRecordings', as }); // delete location with provided id -export const deleteRecording = createAsyncThunk('recordings/deleteRecording', async (id: any, { dispatch }) => { +export const deleteRecording = createAsyncThunk('recordings/deleteRecording', async (id: string, { dispatch }) => { // API call for deleting a location axios .delete(`/admin-ng/capture-agents/${id}`) @@ -121,7 +122,7 @@ const recordingSlice = createSlice({ setRecordingsColumns(state, action: PayloadAction< RecordingState["columns"] >) { - state.columns = action.payload.updatedColumns; + state.columns = action.payload; }, }, // These are used for thunks diff --git a/app/src/slices/seriesDetailsSlice.ts b/app/src/slices/seriesDetailsSlice.ts index 4c0c26dfd3..9343f4bf4a 100644 --- a/app/src/slices/seriesDetailsSlice.ts +++ b/app/src/slices/seriesDetailsSlice.ts @@ -17,6 +17,7 @@ import { transformToIdValueArray } from "../utils/utils"; import { NOTIFICATION_CONTEXT } from "../configs/modalConfig"; import { RootState } from '../store'; import { Statistics, fetchStatistics, fetchStatisticsValueUpdate } from './statisticsSlice'; +import { Ace, TransformedAcl, TransformedAcls } from './aclDetailsSlice'; /** * This file contains redux reducer for actions affecting the state of a series @@ -107,7 +108,7 @@ const initialState: SeriesDetailsState = { }; // fetch metadata of certain series from server -export const fetchSeriesDetailsMetadata = createAsyncThunk('seriesDetails/fetchSeriesDetailsMetadata', async (id: any) => { +export const fetchSeriesDetailsMetadata = createAsyncThunk('seriesDetails/fetchSeriesDetailsMetadata', async (id: string) => { const res = await axios.get(`/admin-ng/series/${id}/metadata.json`); const metadataResponse = res.data; @@ -129,7 +130,7 @@ export const fetchSeriesDetailsMetadata = createAsyncThunk('seriesDetails/fetchS }); // fetch acls of certain series from server -export const fetchSeriesDetailsAcls = createAsyncThunk('seriesDetails/fetchSeriesDetailsAcls', async (id: any, {dispatch}) => { +export const fetchSeriesDetailsAcls = createAsyncThunk('seriesDetails/fetchSeriesDetailsAcls', async (id: string, {dispatch}) => { const res = await axios.get(`/admin-ng/series/${id}/access.json`); const response = res.data; @@ -145,29 +146,22 @@ export const fetchSeriesDetailsAcls = createAsyncThunk('seriesDetails/fetchSerie ); } - let seriesAcls: any[] = []; + let seriesAcls: TransformedAcls = []; if (!!response.series_access) { const json = JSON.parse(response.series_access.acl).acl.ace; - let policies = {}; -// @ts-expect-error TS(7034): Variable 'policyRoles' implicitly has type 'any[]'... Remove this comment to see the full error message - let policyRoles = []; -// @ts-expect-error TS(7006): Parameter 'policy' implicitly has an 'any' type. - json.forEach((policy) => { -// @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message + let policies: { [key: string]: TransformedAcl } = {}; + let policyRoles: string[] = []; + json.forEach((policy: Ace) => { if (!policies[policy.role]) { -// @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message policies[policy.role] = createPolicy(policy.role); policyRoles.push(policy.role); } if (policy.action === "read" || policy.action === "write") { -// @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message policies[policy.role][policy.action] = policy.allow; - } else if (policy.allow === true || policy.allow === "true") { -// @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message + } else if (policy.allow === true) { //|| policy.allow === "true") { policies[policy.role].actions.push(policy.action); } }); -// @ts-expect-error TS(7005): Variable 'policyRoles' implicitly has an 'any[]' t... Remove this comment to see the full error message seriesAcls = policyRoles.map((role) => policies[role]); } @@ -175,7 +169,7 @@ export const fetchSeriesDetailsAcls = createAsyncThunk('seriesDetails/fetchSerie }); // fetch feeds of certain series from server -export const fetchSeriesDetailsFeeds = createAsyncThunk('seriesDetails/fetchSeriesDetailsFeeds', async (id: any) => { +export const fetchSeriesDetailsFeeds = createAsyncThunk('seriesDetails/fetchSeriesDetailsFeeds', async (id: string) => { const res = await axios.get("/admin-ng/feeds/feeds"); const feedsResponse = res.data; @@ -214,7 +208,7 @@ export const fetchSeriesDetailsFeeds = createAsyncThunk('seriesDetails/fetchSeri }); // fetch theme of certain series from server -export const fetchSeriesDetailsTheme = createAsyncThunk('seriesDetails/fetchSeriesDetailsTheme', async (id: any) => { +export const fetchSeriesDetailsTheme = createAsyncThunk('seriesDetails/fetchSeriesDetailsTheme', async (id: string) => { const res = await axios.get(`/admin-ng/series/${id}/theme.json`); const themeResponse = res.data; @@ -241,7 +235,22 @@ export const fetchSeriesDetailsThemeNames = createAsyncThunk('seriesDetails/fetc }); // update series with new metadata -export const updateSeriesMetadata = createAsyncThunk('seriesDetails/updateSeriesMetadata', async (params: {id: any, values: any}, {dispatch, getState}) => { +export const updateSeriesMetadata = createAsyncThunk('seriesDetails/updateSeriesMetadata', async (params: { + id: string, + values: { + contributor: string[], + createdBy: string, + creator: string[], + description: String, + identifier: string, + language: string, + license: string, + publisher: string[], + rightsHolder: string, + subject: string, + title: string, + } +}, {dispatch, getState}) => { const { id, values } = params; let metadataInfos = getSeriesDetailsMetadata(getState() as RootState); @@ -262,7 +271,15 @@ export const updateSeriesMetadata = createAsyncThunk('seriesDetails/updateSeries }); // update series with new metadata -export const updateExtendedSeriesMetadata = createAsyncThunk('seriesDetails/updateExtendedSeriesMetadata', async (params: {id: any, values: any, catalog: any}, {dispatch, getState}) => { +export const updateExtendedSeriesMetadata = createAsyncThunk('seriesDetails/updateExtendedSeriesMetadata', async (params: { + id: string, + values: { [key: string]: any }, + catalog: { + flavor: string, + title: string, + fields: { [key: string]: any }[] + } +}, {dispatch, getState}) => { const { id, values, catalog } = params; const { fields, data, headers } = transformMetadataForUpdate( @@ -296,14 +313,16 @@ export const updateExtendedSeriesMetadata = createAsyncThunk('seriesDetails/upda dispatch(setSeriesDetailsExtendedMetadata(newExtendedMetadata)); }); -export const updateSeriesAccess = createAsyncThunk('seriesDetails/updateSeriesAccess', async (params: {id: any, policies: any}, {dispatch}) => { +export const updateSeriesAccess = createAsyncThunk('seriesDetails/updateSeriesAccess', async (params: { + id: string, + policies: { [key: string]: TransformedAcl } +}, {dispatch}) => { const { id, policies } = params; let data = new URLSearchParams(); data.append("acl", JSON.stringify(policies)); -// @ts-expect-error TS(2345): Argument of type 'boolean' is not assignable to pa... Remove this comment to see the full error message - data.append("override", true); + data.append("override", String(true)); return axios .post(`/admin-ng/series/${id}/access`, data, { @@ -339,7 +358,10 @@ export const updateSeriesAccess = createAsyncThunk('seriesDetails/updateSeriesAc }); }); -export const updateSeriesTheme = createAsyncThunk('seriesDetails/updateSeriesTheme', async (params: {id: any, values: any}, {dispatch, getState}) => { +export const updateSeriesTheme = createAsyncThunk('seriesDetails/updateSeriesTheme', async (params: { + id: string, + values: { theme: string}, +}, {dispatch, getState}) => { const { id, values } = params; let themeNames = getSeriesDetailsThemeNames(getState() as RootState); @@ -386,13 +408,8 @@ export const updateSeriesTheme = createAsyncThunk('seriesDetails/updateSeriesThe }); }); -// TODO: FIX STATISTICS WHEN MODERNIZING REDUX TOOLKIT FOR EVENTS - // thunks for statistics -// This is probably not the optimal way to update these thunks to reduxToolkit, but -// it works for now - -export const fetchSeriesStatistics = createAsyncThunk('seriesDetails/fetchSeriesStatistics', async (seriesId: any, {getState}) => { +export const fetchSeriesStatistics = createAsyncThunk('seriesDetails/fetchSeriesStatistics', async (seriesId: string, {getState}) => { // get prior statistics const state = getState(); const statistics = getStatistics(state as RootState); @@ -406,9 +423,14 @@ export const fetchSeriesStatistics = createAsyncThunk('seriesDetails/fetchSeries ); }); -// TODO: FIX STATISTICS WHEN MODERNIZING REDUX TOOLKIT FOR EVENTS - -export const fetchSeriesStatisticsValueUpdate = createAsyncThunk('seriesDetails/fetchSeriesStatisticsValueUpdate', async (params: {seriesId: any, providerId: any, from: any, to: any, dataResolution: any, timeMode: any}, {getState}) => { +export const fetchSeriesStatisticsValueUpdate = createAsyncThunk('seriesDetails/fetchSeriesStatisticsValueUpdate', async (params: { + seriesId: string, + providerId: string, + from: string, + to: string, + dataResolution: string[], + timeMode: any +}, {getState}) => { const {seriesId, providerId, from, to, dataResolution, timeMode } = params; // get prior statistics diff --git a/app/src/slices/seriesSlice.ts b/app/src/slices/seriesSlice.ts index 7d8656bd50..d25acd709e 100644 --- a/app/src/slices/seriesSlice.ts +++ b/app/src/slices/seriesSlice.ts @@ -13,6 +13,8 @@ import { transformToObjectArray, } from "../utils/utils"; import { addNotification } from '../slices/notificationSlice'; +import { TableConfig } from '../configs/tableConfigs/aclsTableConfig'; +import { TransformedAcls } from './aclDetailsSlice'; /** * This file contains redux reducer for actions affecting the state of series @@ -59,7 +61,7 @@ type SeriesState = { statusThemes: 'uninitialized' | 'loading' | 'succeeded' | 'failed', errorThemes: SerializedError | null, results: Series[], - columns: any, // TODO: proper typing, derive from `initialColumns` + columns: TableConfig["columns"], showActions: boolean, total: number, count: number, @@ -149,7 +151,23 @@ export const fetchSeriesThemes = createAsyncThunk('series/fetchSeriesThemes', as }); // post new series to backend -export const postNewSeries = createAsyncThunk('series/postNewSeries', async (params: {values: any, metadataInfo: any, extendedMetadata: any}, {dispatch}) => { +export const postNewSeries = createAsyncThunk('series/postNewSeries', async (params: { + values: { + acls: TransformedAcls, + contributor: string[], + creator: string[], + description: string, + language: string, + license: string, + publisher: string[], + rightsHolder: string, + subject: string, + theme: string, + title: string, + }, + metadataInfo: MetadataCatalog, + extendedMetadata: MetadataCatalog[] +}, {dispatch}) => { const { values, metadataInfo, extendedMetadata } = params let metadataFields, extendedMetadataFields, metadata, access; @@ -214,7 +232,7 @@ export const postNewSeries = createAsyncThunk('series/postNewSeries', async (par }); // check for events of the series and if deleting the series if it has events is allowed -export const checkForEventsDeleteSeriesModal = createAsyncThunk('series/checkForEventsDeleteSeriesModal', async (id: any, {dispatch}) => { +export const checkForEventsDeleteSeriesModal = createAsyncThunk('series/checkForEventsDeleteSeriesModal', async (id: string, {dispatch}) => { const hasEventsRequest = await axios.get( `/admin-ng/series/${id}/hasEvents.json` ); @@ -234,7 +252,7 @@ export const checkForEventsDeleteSeriesModal = createAsyncThunk('series/checkFor }); // delete series with provided id -export const deleteSeries = createAsyncThunk('series/deleteSeries', async (id: any, {dispatch}) => { +export const deleteSeries = createAsyncThunk('series/deleteSeries', async (id: string, {dispatch}) => { // API call for deleting a series axios .delete(`/admin-ng/series/${id}`) @@ -251,7 +269,18 @@ export const deleteSeries = createAsyncThunk('series/deleteSeries', async (id: a }); // delete series with provided ids -export const deleteMultipleSeries = createAsyncThunk('series/deleteMultipleSeries', async (series: any, {dispatch}) => { +export const deleteMultipleSeries = createAsyncThunk('series/deleteMultipleSeries', async ( + series: { + contributors: string[], + createdBy: string, + creation_date: string, + hasEvents: false, + id: string, + organizers: string[], + selected: boolean, + title: string, + }[], +{dispatch}) => { let data = []; for (let i = 0; i < series.length; i++) { @@ -289,8 +318,7 @@ export const fetchSeriesOptions = async () => { }; // Check if a series has events -// @ts-expect-error TS(7006): Parameter 'seriesId' implicitly has an 'any' type. -export const hasEvents = async (seriesId) => { +export const hasEvents = async (seriesId: string) => { let data = await axios.get(`/admin-ng/series/${seriesId}/hasEvents.json`); return (await data.data).hasEvents; diff --git a/app/src/slices/serverSlice.ts b/app/src/slices/serverSlice.ts index fa66c16ca1..22c8949042 100644 --- a/app/src/slices/serverSlice.ts +++ b/app/src/slices/serverSlice.ts @@ -2,6 +2,7 @@ import { PayloadAction, SerializedError, createAsyncThunk, createSlice } from '@ import { serversTableConfig } from "../configs/tableConfigs/serversTableConfig"; import axios from 'axios'; import { getURLParams } from '../utils/resourceUtils'; +import { TableConfig } from '../configs/tableConfigs/aclsTableConfig'; /** * This file contains redux reducer for actions affecting the state of servers @@ -20,7 +21,7 @@ type ServerState = { status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', error: SerializedError | null, results: Server[], - columns: any, // TODO: proper typing, derive from `initialColumns` + columns: TableConfig["columns"], total: number, count: number, offset: number, @@ -58,11 +59,14 @@ export const fetchServers = createAsyncThunk('servers/fetchServers', async (_, { }); // change maintenance status of a server/host -export const setServerMaintenance = createAsyncThunk('servers/setServerMaintenance', async (params: {host: any, maintenance: any}) => { +export const setServerMaintenance = createAsyncThunk('servers/setServerMaintenance', async (params: { + host: string, + maintenance: boolean +}) => { const { host, maintenance } = params; let data = new URLSearchParams(); data.append("host", host); - data.append("maintenance", maintenance); + data.append("maintenance", String(maintenance)); axios .post("/services/maintenance", data) @@ -81,7 +85,7 @@ const serverSlice = createSlice({ setServerColumns(state, action: PayloadAction< ServerState["columns"] >) { - state.columns = action.payload.updatedColumns; + state.columns = action.payload; }, }, // These are used for thunks diff --git a/app/src/slices/serviceSlice.ts b/app/src/slices/serviceSlice.ts index 16e21b52c2..67e6b16f09 100644 --- a/app/src/slices/serviceSlice.ts +++ b/app/src/slices/serviceSlice.ts @@ -2,6 +2,7 @@ import { PayloadAction, SerializedError, createAsyncThunk, createSlice } from '@ import { servicesTableConfig } from "../configs/tableConfigs/servicesTableConfig"; import axios from 'axios'; import { getURLParams } from '../utils/resourceUtils'; +import { TableConfig } from '../configs/tableConfigs/aclsTableConfig'; /** * This file contains redux reducer for actions affecting the state of services @@ -22,7 +23,7 @@ type ServiceState = { status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', error: SerializedError | null, results: Service[], - columns: any, // TODO: proper typing, derive from `initialColumns` + columns: TableConfig["columns"], total: number, count: number, offset: number, @@ -59,7 +60,10 @@ export const fetchServices = createAsyncThunk('services/fetchServices', async (_ }); // restarts a service after initiated by user -export const restartService = createAsyncThunk('services/fetchServices', async (params: {host: any, serviceType: any}) => { +export const restartService = createAsyncThunk('services/fetchServices', async (params: { + host: string, + serviceType: string +}) => { const { host, serviceType } = params let data = new URLSearchParams(); data.append("host", host); @@ -82,7 +86,7 @@ const serviceSlice = createSlice({ setServiceColumns(state, action: PayloadAction< ServiceState["columns"] >) { - state.columns = action.payload.updatedColumns; + state.columns = action.payload; }, }, // These are used for thunks diff --git a/app/src/slices/themeDetailsSlice.ts b/app/src/slices/themeDetailsSlice.ts index 4e362df285..75273b84a5 100644 --- a/app/src/slices/themeDetailsSlice.ts +++ b/app/src/slices/themeDetailsSlice.ts @@ -72,7 +72,7 @@ const initialState: ThemeDetailsState = { }; // fetch details of certain theme from server -export const fetchThemeDetails = createAsyncThunk('themeDetails/fetchThemeDetails', async (id: any, {dispatch}) => { +export const fetchThemeDetails = createAsyncThunk('themeDetails/fetchThemeDetails', async (id: number) => { // 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. @@ -81,13 +81,16 @@ export const fetchThemeDetails = createAsyncThunk('themeDetails/fetchThemeDetail }); // fetch usage of a certain theme -export const fetchUsage = createAsyncThunk('themeDetails/fetchUsage', async (id: any, {dispatch}) => { +export const fetchUsage = createAsyncThunk('themeDetails/fetchUsage', async (id: number) => { const res = await axios.get(`/admin-ng/themes/${id}/usage.json`); return res.data; }); // update a certain theme -export const updateThemeDetails = createAsyncThunk('themeDetails/updateThemeDetails', async (params: {id: any, values: any}, {dispatch}) => { +export const updateThemeDetails = createAsyncThunk('themeDetails/updateThemeDetails', async (params: { + id: number, + values: Details +}, {dispatch}) => { const { values, id } = params let data = buildThemeBody(values); diff --git a/app/src/slices/themeSlice.ts b/app/src/slices/themeSlice.ts index 9fba15cf77..a1c4be18e7 100644 --- a/app/src/slices/themeSlice.ts +++ b/app/src/slices/themeSlice.ts @@ -3,6 +3,7 @@ import { themesTableConfig } from "../configs/tableConfigs/themesTableConfig"; import axios from 'axios'; import { buildThemeBody, getURLParams } from '../utils/resourceUtils'; import { addNotification } from '../slices/notificationSlice'; +import { TableConfig } from '../configs/tableConfigs/aclsTableConfig'; /** * This file contains redux reducer for actions affecting the state of themes @@ -33,7 +34,7 @@ type ThemeState = { status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', error: SerializedError | null, results: Details[], - columns: any, // TODO: proper typing, derive from `initialColumns` + columns: TableConfig["columns"], total: number, count: number, offset: number, @@ -71,7 +72,7 @@ export const fetchThemes = createAsyncThunk('theme/fetchThemes', async (_, { get }); // post new theme to backend -export const postNewTheme = createAsyncThunk('theme/postNewTheme', async (values: any, {dispatch}) => { +export const postNewTheme = createAsyncThunk('theme/postNewTheme', async (values: Details, {dispatch}) => { // get URL params used for post request let data = buildThemeBody(values); @@ -95,7 +96,7 @@ export const postNewTheme = createAsyncThunk('theme/postNewTheme', async (values }); // delete theme with provided id -export const deleteTheme = createAsyncThunk('theme/deleteTheme', async (id: any, {dispatch}) => { +export const deleteTheme = createAsyncThunk('theme/deleteTheme', async (id: number, {dispatch}) => { axios .delete(`/admin-ng/themes/${id}`) .then((res) => { diff --git a/app/src/slices/userDetailsSlice.ts b/app/src/slices/userDetailsSlice.ts index 336378496b..4cc73becf4 100644 --- a/app/src/slices/userDetailsSlice.ts +++ b/app/src/slices/userDetailsSlice.ts @@ -30,7 +30,7 @@ const initialState: UserDetailsState = { }; // fetch details about certain user from server -export const fetchUserDetails = createAsyncThunk('userDetails/fetchUserDetails', async (username: any) => { +export const fetchUserDetails = createAsyncThunk('userDetails/fetchUserDetails', async (username: string) => { // 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. @@ -39,7 +39,10 @@ export const fetchUserDetails = createAsyncThunk('userDetails/fetchUserDetails', }); // update existing user with changed values -export const updateUserDetails = createAsyncThunk('userDetails/updateUserDetails', async (params: {values: any, username: string}, {dispatch}) => { +export const updateUserDetails = createAsyncThunk('userDetails/updateUserDetails', async (params: { + values: UserDetailsState, + username: string +}, {dispatch}) => { const { username, values } = params // get URL params used for put request diff --git a/app/src/slices/userSlice.ts b/app/src/slices/userSlice.ts index bbe8cbf51a..d51eb2ea35 100644 --- a/app/src/slices/userSlice.ts +++ b/app/src/slices/userSlice.ts @@ -4,6 +4,7 @@ import axios from 'axios'; import { transformToIdValueArray } from "../utils/utils"; import { buildUserBody, getURLParams } from "../utils/resourceUtils"; import { addNotification } from '../slices/notificationSlice'; +import { TableConfig } from '../configs/tableConfigs/aclsTableConfig'; /** * This file contains redux reducer for actions affecting the state of users @@ -21,7 +22,7 @@ type UsersState = { status: 'uninitialized' | 'loading' | 'succeeded' | 'failed', error: SerializedError | null, results: UserResult[], - columns: any, // TODO: proper typing, derive from `initialColumns` + columns: TableConfig["columns"], total: number, count: number, offset: number, @@ -58,7 +59,7 @@ export const fetchUsers = createAsyncThunk('users/fetchUsers', async (_, { getSt }); // new user to backend -export const postNewUser = createAsyncThunk('users/postNewUser', async (values: any, {dispatch}) => { +export const postNewUser = createAsyncThunk('users/postNewUser', async (values: UserResult, {dispatch}) => { // get URL params used for post request let data = buildUserBody(values); @@ -82,7 +83,7 @@ export const postNewUser = createAsyncThunk('users/postNewUser', async (values: }); // delete user with provided id -export const deleteUser = createAsyncThunk('users/deleteUser', async (id: any, {dispatch}) => { +export const deleteUser = createAsyncThunk('users/deleteUser', async (id: string, {dispatch}) => { // API call for deleting an user axios .delete(`/admin-ng/users/${id}.json`) diff --git a/app/src/slices/workflowSlice.ts b/app/src/slices/workflowSlice.ts index de571e9acd..d574a15530 100644 --- a/app/src/slices/workflowSlice.ts +++ b/app/src/slices/workflowSlice.ts @@ -38,7 +38,7 @@ const initialState: WorkflowState = { }; // fetch workflow definitions from server -export const fetchWorkflowDef = createAsyncThunk('workflow/fetchWorkflowDef', async (type: any) => { +export const fetchWorkflowDef = createAsyncThunk('workflow/fetchWorkflowDef', async (type: string) => { let urlParams; switch (type) {