diff --git a/src/components/events/partials/ModalTabsAndPages/EventDetailsSchedulingTab.tsx b/src/components/events/partials/ModalTabsAndPages/EventDetailsSchedulingTab.tsx index bfba0c5ef0..f320af7408 100644 --- a/src/components/events/partials/ModalTabsAndPages/EventDetailsSchedulingTab.tsx +++ b/src/components/events/partials/ModalTabsAndPages/EventDetailsSchedulingTab.tsx @@ -36,6 +36,7 @@ import { NOTIFICATION_CONTEXT } from "../../../../configs/modalConfig"; import { useAppDispatch, useAppSelector } from "../../../../store"; import { checkConflicts, + getSelectedCaptureOptions, saveSchedulingInfo, SchedulingInfo, } from "../../../../slices/eventDetailsSlice"; @@ -43,18 +44,25 @@ import { removeNotificationWizardForm, addNotification, } from "../../../../slices/notificationSlice"; -import { Recording } from "../../../../slices/recordingSlice"; +import { + CaptureOptionSelection, + captureOptions, + contentOverridesInputs, + Recording, +} from "../../../../slices/recordingSlice"; import { useTranslation } from "react-i18next"; import WizardNavigationButtons from "../../../shared/wizard/WizardNavigationButtons"; import SchedulingTime from "../wizards/scheduling/SchedulingTime"; import SchedulingLocation from "../wizards/scheduling/SchedulingLocation"; import SchedulingInputs from "../wizards/scheduling/SchedulingInputs"; +import SchedulingCaptureOptions from "../wizards/scheduling/SchedulingCaptureOptions"; + import SchedulingConflicts from "../wizards/scheduling/SchedulingConflicts"; import { ParseKeys } from "i18next"; import ModalContentTable from "../../../shared/modals/ModalContentTable"; import i18n from "../../../../i18n/i18n"; -export type InitialValues = { +export type InitialValues = CaptureOptionSelection & { scheduleStartDate: string; scheduleStartHour: string; scheduleStartMinute: string; @@ -143,10 +151,23 @@ const EventDetailsSchedulingTab = ({ return value ? t(value as ParseKeys) : ""; }; - // changes the inputs in the formik + // finds the agent, whose options are to be displayed in the formik + const getAgent = (deviceId: Recording["id"]) => { + if (deviceId === source.device.id) { + return source.device; + } + return filterDevicesForAccess(user, captureAgents).find(agent => agent.id === deviceId); + }; + + // changes the inputs and other options in the formik const changeInputs = (deviceId: Recording["id"], setFieldValue: FormikHelpers["setFieldValue"]) => { + const agent = getAgent(deviceId); setFieldValue("captureAgent", deviceId); - setFieldValue("inputs", []); + // Like in the wizard, all inputs and options of the agent are selected + setFieldValue("inputs", getInputs(deviceId).map(input => input.id)); + for (const { field, list } of captureOptions) { + setFieldValue(field, agent?.[list]?.map(option => option.id) ?? []); + } }; const filterCaptureAgents = (agent: Recording) => { return agent.id === source.agentId || hasDeviceAccess(user, agent.id); @@ -161,19 +182,15 @@ const EventDetailsSchedulingTab = ({ accessAllowed(formik.values.captureAgent) && !(conflicts.length > 0) ) { - // check if user provided values differ from initial ones - if (!_.isEqual(formik.values, formik.initialValues)) { - if (!_.isEqual(formik.values.inputs, formik.initialValues.inputs)) { - return !_.isEqual( - [...formik.values.inputs].sort(), - [...formik.initialValues.inputs].sort(), - ); - } else { - return true; - } - } else { - return false; - } + // check if user provided values differ from initial ones, the order of selected options does not matter + const sortSelected = (values: InitialValues) => ({ + ...values, + inputs: [...values.inputs].sort(), + streamConfiguration: [...values.streamConfiguration].sort(), + devicePosition: [...values.devicePosition].sort(), + deviceContent: [...values.deviceContent].sort(), + }); + return !_.isEqual(sortSelected(formik.values), sortSelected(formik.initialValues)); } else { return false; } @@ -231,6 +248,7 @@ const EventDetailsSchedulingTab = ({ scheduleEndMinute: source.end.minute != null ? makeTwoDigits(source.end.minute) : "", captureAgent: source.device.name, inputs: inputs.filter(input => input !== ""), + ...getSelectedCaptureOptions(source.agentConfiguration), }; }; @@ -491,31 +509,40 @@ const EventDetailsSchedulingTab = ({ } - {/* inputs */} - - - {t( - "EVENTS.EVENTS.DETAILS.SOURCE.PLACEHOLDER.INPUTS", - )} - - - {!!formik.values.captureAgent && - !!getInputs(formik.values.captureAgent) && - getInputs(formik.values.captureAgent).length > - 0 && - (hasAccessRole && - accessAllowed(formik.values.captureAgent) - ? - : formik.values.inputs.map((input, key) => ( - - {getInputForAgent(formik.values.captureAgent, input)} -
-
- )))} - - + {/* inputs, which are not offered if the content configuration determines them */} + {!contentOverridesInputs(getAgent(formik.values.captureAgent)) && ( + + + {t( + "EVENTS.EVENTS.DETAILS.SOURCE.PLACEHOLDER.INPUTS", + )} + + + {!!formik.values.captureAgent && + !!getInputs(formik.values.captureAgent) && + getInputs(formik.values.captureAgent).length > + 0 && + (hasAccessRole && + accessAllowed(formik.values.captureAgent) + ? + : formik.values.inputs.map((input, key) => ( + + {getInputForAgent(formik.values.captureAgent, input)} +
+
+ )))} + + + )} + + {/* other options the agent offers */} + diff --git a/src/components/events/partials/ModalTabsAndPages/NewSourcePage.tsx b/src/components/events/partials/ModalTabsAndPages/NewSourcePage.tsx index fd495a2abe..aa94d18a34 100644 --- a/src/components/events/partials/ModalTabsAndPages/NewSourcePage.tsx +++ b/src/components/events/partials/ModalTabsAndPages/NewSourcePage.tsx @@ -36,7 +36,13 @@ import { changeStartMinuteMultiple, } from "../../../../utils/dateUtils"; import { useAppDispatch, useAppSelector } from "../../../../store"; -import { Recording, fetchRecordings } from "../../../../slices/recordingSlice"; +import { + CaptureOptionSelection, + Recording, + captureOptions, + contentOverridesInputs, + fetchRecordings, +} from "../../../../slices/recordingSlice"; import { addNotification, removeNotificationWizardForm } from "../../../../slices/notificationSlice"; import { parseISO } from "date-fns"; import WizardNavigationButtons from "../../../shared/wizard/WizardNavigationButtons"; @@ -46,6 +52,7 @@ import ButtonLikeAnchor from "../../../shared/ButtonLikeAnchor"; import SchedulingTime from "../wizards/scheduling/SchedulingTime"; import SchedulingLocation from "../wizards/scheduling/SchedulingLocation"; import SchedulingInputs from "../wizards/scheduling/SchedulingInputs"; +import SchedulingCaptureOptions from "../wizards/scheduling/SchedulingCaptureOptions"; import SchedulingConflicts from "../wizards/scheduling/SchedulingConflicts"; import { ParseKeys } from "i18next"; import { LuCircleX } from "react-icons/lu"; @@ -54,7 +61,7 @@ import i18n from "../../../../i18n/i18n"; /** * This component renders the source page for new events in the new event wizard. */ -interface RequiredFormProps { +interface RequiredFormProps extends CaptureOptionSelection { scheduleStartDate: string, sourceMode: string, // Schedule @@ -393,7 +400,7 @@ const Upload = ({ /* * Renders fields for providing information for schedule of event */ -const Schedule = name === formik.values.location); + const renderInputDeviceOptions = () => { if (formik.values.location) { - const inputDevice = inputDevices.find( - ({ name }) => name === formik.values.location, - ); - if (!inputDevice) { + if (!selectedDevice) { return <>; } return ( ); } @@ -759,17 +765,30 @@ const Schedule = input.id)); } + // Select all options of the location + for (const { field, list } of captureOptions) { + await formik.setFieldValue(field, inputDevice ? inputDevice[list].map(option => option.id) : []); + } // Set location await formik.setFieldValue("location", value); }} /> - - {t("EVENTS.EVENTS.NEW.SOURCE.PLACEHOLDER.INPUTS")} - - {/* Render checkbox for each input option of the selected input device*/} - {renderInputDeviceOptions()} - - + {/* The inputs are not offered, if the content configuration determines them */} + {!contentOverridesInputs(selectedDevice) && ( + + {t("EVENTS.EVENTS.NEW.SOURCE.PLACEHOLDER.INPUTS")} + + {/* Render checkbox for each input option of the selected input device*/} + {renderInputDeviceOptions()} + + + )} + {/* Other options the selected location offers */} + diff --git a/src/components/events/partials/wizards/scheduling/SchedulingCaptureOptions.tsx b/src/components/events/partials/wizards/scheduling/SchedulingCaptureOptions.tsx new file mode 100644 index 0000000000..bceec339c7 --- /dev/null +++ b/src/components/events/partials/wizards/scheduling/SchedulingCaptureOptions.tsx @@ -0,0 +1,55 @@ +import { useTranslation } from "react-i18next"; +import SchedulingInputs from "./SchedulingInputs"; +import { + CaptureOptionLists, + CaptureOptionSelection, + captureOptions, +} from "../../../../../slices/recordingSlice"; + +/** + * Renders a row for every option of a capture agent that can be chosen for an event, in the same way as the inputs + */ +const SchedulingCaptureOptions = ({ + agent, + selection, + disabled, +}: { + agent: CaptureOptionLists | undefined, + selection: CaptureOptionSelection, + // Only show what is selected + disabled: boolean, +}) => { + const { t } = useTranslation(); + + return ( + <> + {captureOptions.map(({ field, list, label }) => { + const options = agent?.[list] ?? []; + if (options.length === 0) { + return null; + } + + return ( + + {t(label)} + + {field === "deviceContent" && ( +

{t("CAPTURE_AGENT.OPTIONS.CONTENT_NOTICE")}

+ )} + {disabled + ? options.filter(option => selection[field].includes(option.id)).map(option => ( + + {t(option.value, option.id)} +
+
+ )) + : } + + + ); + })} + + ); +}; + +export default SchedulingCaptureOptions; diff --git a/src/components/events/partials/wizards/scheduling/SchedulingInputs.tsx b/src/components/events/partials/wizards/scheduling/SchedulingInputs.tsx index 25e49ba54c..30ad422c5c 100644 --- a/src/components/events/partials/wizards/scheduling/SchedulingInputs.tsx +++ b/src/components/events/partials/wizards/scheduling/SchedulingInputs.tsx @@ -2,8 +2,11 @@ import { useTranslation } from "react-i18next"; import { Field } from "formik"; const SchedulingInputs = ({ + name = "inputs", inputs, }: { + // Name of the form field, that holds the ids of the selected inputs + name?: string, inputs: { id: string, value: string @@ -17,7 +20,7 @@ const SchedulingInputs = ({ (input, key) => (