diff --git a/src/components/events/partials/ModalTabsAndPages/EventDetailsSchedulingTab.tsx b/src/components/events/partials/ModalTabsAndPages/EventDetailsSchedulingTab.tsx index 60345a8bf8..e41c6cc945 100644 --- a/src/components/events/partials/ModalTabsAndPages/EventDetailsSchedulingTab.tsx +++ b/src/components/events/partials/ModalTabsAndPages/EventDetailsSchedulingTab.tsx @@ -1,7 +1,7 @@ import React, { useEffect } from "react"; import _ from "lodash"; import DatePicker from "react-datepicker"; -import { Formik, FormikErrors, FormikProps } from "formik"; +import { Formik, FormikErrors, FormikProps, FormikValues } from "formik"; import Notifications from "../../../shared/Notifications"; import { getSchedulingConflicts, @@ -52,6 +52,8 @@ import SchedulingInputs from "../wizards/scheduling/SchedulingInputs"; import SchedulingConflicts from "../wizards/scheduling/SchedulingConflicts"; import { ParseKeys } from "i18next"; import ModalContentTable from "../../../shared/modals/ModalContentTable"; +import SchedulingRadio from "../wizards/scheduling/SchedulingRadio"; +import * as Yup from "yup"; export type InitialValues = { scheduleStartDate: string; @@ -64,6 +66,10 @@ export type InitialValues = { scheduleEndMinute: string; captureAgent: string; inputs: string[]; + stream: string; + record: string; + layout: string; + cameraPosition: string; } /** @@ -124,11 +130,63 @@ const EventDetailsSchedulingTab = ({ // finds the inputs to be displayed in the formik const getInputs = (deviceId: Recording["id"]) => { if (deviceId === source.device.id) { - return source.device.inputs ? source.device.inputs : []; + return source.device.parsedCapabilities.inputs ? source.device.parsedCapabilities.inputs : []; } else { for (const agent of filterDevicesForAccess(user, captureAgents)) { if (agent.id === deviceId) { - return agent.inputs ? agent.inputs : []; + return agent.parsedCapabilities.inputs ? agent.parsedCapabilities.inputs : []; + } + } + return []; + } + }; + + const getStream = (deviceId: Recording["id"]) => { + if (deviceId === source.device.id) { + return source.device.parsedCapabilities.stream ? source.device.parsedCapabilities.stream : []; + } else { + for (const agent of filterDevicesForAccess(user, captureAgents)) { + if (agent.id === deviceId) { + return agent.parsedCapabilities.stream ? agent.parsedCapabilities.stream : []; + } + } + return []; + } + }; + + const getRecord = (deviceId: Recording["id"]) => { + if (deviceId === source.device.id) { + return source.device.parsedCapabilities.record ? source.device.parsedCapabilities.record : []; + } else { + for (const agent of filterDevicesForAccess(user, captureAgents)) { + if (agent.id === deviceId) { + return agent.parsedCapabilities.record ? agent.parsedCapabilities.record : []; + } + } + return []; + } + }; + + const getLayout = (deviceId: Recording["id"]) => { + if (deviceId === source.device.id) { + return source.device.parsedCapabilities.layout ? source.device.parsedCapabilities.layout : []; + } else { + for (const agent of filterDevicesForAccess(user, captureAgents)) { + if (agent.id === deviceId) { + return agent.parsedCapabilities.layout ? agent.parsedCapabilities.layout : []; + } + } + return []; + } + }; + + const getCameraPosition = (deviceId: Recording["id"]) => { + if (deviceId === source.device.id) { + return source.device.parsedCapabilities.cameraPosition ? source.device.parsedCapabilities.cameraPosition : []; + } else { + for (const agent of filterDevicesForAccess(user, captureAgents)) { + if (agent.id === deviceId) { + return agent.parsedCapabilities.cameraPosition ? agent.parsedCapabilities.cameraPosition : []; } } return []; @@ -141,6 +199,30 @@ const EventDetailsSchedulingTab = ({ return value ? t(value as ParseKeys) : ""; }; + const getStreamForAgent = (deviceId: Recording["id"], s: string) => { + const stream = getStream(deviceId); + const value = stream.find(agent => agent.id === s)?.value; + return value ? t(value as ParseKeys) : ""; + }; + + const getRecordForAgent = (deviceId: Recording["id"], s: string) => { + const record = getRecord(deviceId); + const value = record.find(agent => agent.id === s)?.value; + return value ? t(value as ParseKeys) : ""; + }; + + const getLayoutForAgent = (deviceId: Recording["id"], s: string) => { + const layout = getLayout(deviceId); + const value = layout.find(agent => agent.id === s)?.value; + return value ? t(value as ParseKeys) : ""; + }; + + const getCameraPositionForAgent = (deviceId: Recording["id"], s: string) => { + const cameraPosition = getCameraPosition(deviceId); + const value = cameraPosition.find(agent => agent.id === s)?.value; + return value ? t(value as ParseKeys) : ""; + }; + // changes the inputs in the formik const changeInputs = (deviceId: Recording["id"], setFieldValue: (field: string, value: any) => Promise>) => { setFieldValue("captureAgent", deviceId); @@ -206,14 +288,56 @@ const EventDetailsSchedulingTab = ({ ); }; + const validationSchema = Yup.object({ + // Capture agent specific validation + stream: Yup.string() + .nullable() + .oneOf(["0", "1"], "Invalid stream value") + .optional(), + record: Yup.string() + .nullable() + .oneOf(["0", "1"], "Invalid record value") + .optional() + .test( + "record-depends-on-stream", + "Record can be 0 only if Stream is 1", + function (value) { + const { stream } = this.parent as FormikValues; + + // If record is not provided — no validation needed + if (value == null) { return true; } + + // If stream is not provided — also fine + if (stream == null || stream === "") { return true; } + + // Enforce the rule only when both are defined + if (value === "0" && stream !== "1") { return false; } + + return true; + }, + ), + }); + // initial values of the formik form const getInitialValues = () => { const startDate = new Date(source.start.date); const endDate = new Date(source.end.date); - const inputs = source.device.inputMethods - ? Array.from(source.device.inputMethods) + const inputs = source.device.capabilitiesMethods.inputs + ? Array.from(source.device.capabilitiesMethods.inputs) : []; + const stream = source.device.capabilitiesMethods.stream && source.device.capabilitiesMethods.stream.length > 0 + ? source.device.capabilitiesMethods.stream[0] + : ""; + const record = source.device.capabilitiesMethods.record && source.device.capabilitiesMethods.record.length > 0 + ? source.device.capabilitiesMethods.record[0] + : ""; + const layout = source.device.capabilitiesMethods.layout && source.device.capabilitiesMethods.layout.length > 0 + ? source.device.capabilitiesMethods.layout[0] + : ""; + const cameraPosition = source.device.capabilitiesMethods.cameraPosition && source.device.capabilitiesMethods.cameraPosition.length > 0 + ? source.device.capabilitiesMethods.cameraPosition[0] + : ""; startDate.setHours(0, 0, 0); endDate.setHours(0, 0, 0); @@ -229,6 +353,10 @@ const EventDetailsSchedulingTab = ({ scheduleEndMinute: source.end.minute != null ? makeTwoDigits(source.end.minute) : "", captureAgent: source.device.name, inputs: inputs.filter(input => input !== ""), + stream: stream, + record: record, + layout: layout, + cameraPosition: cameraPosition, }; }; @@ -249,6 +377,7 @@ const EventDetailsSchedulingTab = ({ enableReinitialize initialValues={getInitialValues()} + validationSchema={validationSchema} onSubmit={values => submitForm(values)} innerRef={formikRef} > @@ -505,6 +634,7 @@ const EventDetailsSchedulingTab = ({ (hasAccessRole && accessAllowed(formik.values.captureAgent) ? : formik.values.inputs.map((input, key) => ( @@ -515,6 +645,114 @@ const EventDetailsSchedulingTab = ({ )))} + + {/* stream */} + + + {t( + "EVENTS.EVENTS.DETAILS.SOURCE.PLACEHOLDER.STREAM", + )} + + + {!!formik.values.captureAgent && + !!getStream(formik.values.captureAgent) && + getStream(formik.values.captureAgent).length > + 0 && + (hasAccessRole && + accessAllowed(formik.values.captureAgent) + ? + : + + {getStreamForAgent(formik.values.captureAgent, formik.values.stream)} +
+
+ )} + + + + {/* record */} + + + {t( + "EVENTS.EVENTS.DETAILS.SOURCE.PLACEHOLDER.RECORD", + )} + + + {!!formik.values.captureAgent && + !!getRecord(formik.values.captureAgent) && + getRecord(formik.values.captureAgent).length > + 0 && + (hasAccessRole && + accessAllowed(formik.values.captureAgent) + ? + : + + {getRecordForAgent(formik.values.captureAgent, formik.values.record)} +
+
+ )} + + + + {/* layout */} + + + {t( + "EVENTS.EVENTS.DETAILS.SOURCE.PLACEHOLDER.LAYOUT", + )} + + + {!!formik.values.captureAgent && + !!getLayout(formik.values.captureAgent) && + getLayout(formik.values.captureAgent).length > + 0 && + (hasAccessRole && + accessAllowed(formik.values.captureAgent) + ? + : + + {getLayoutForAgent(formik.values.captureAgent, formik.values.layout)} +
+
+ )} + + + + {/* cameraPosition */} + + + {t( + "EVENTS.EVENTS.DETAILS.SOURCE.PLACEHOLDER.CAMERA_POSITION", + )} + + + {!!formik.values.captureAgent && + !!getCameraPosition(formik.values.captureAgent) && + getCameraPosition(formik.values.captureAgent).length > + 0 && + (hasAccessRole && + accessAllowed(formik.values.captureAgent) + ? + : + + {getCameraPositionForAgent(formik.values.captureAgent, formik.values.cameraPosition)} +
+
+ )} + + diff --git a/src/components/events/partials/ModalTabsAndPages/NewSourcePage.tsx b/src/components/events/partials/ModalTabsAndPages/NewSourcePage.tsx index 397dce368e..921867d8af 100644 --- a/src/components/events/partials/ModalTabsAndPages/NewSourcePage.tsx +++ b/src/components/events/partials/ModalTabsAndPages/NewSourcePage.tsx @@ -49,6 +49,7 @@ import SchedulingInputs from "../wizards/scheduling/SchedulingInputs"; import SchedulingConflicts from "../wizards/scheduling/SchedulingConflicts"; import { ParseKeys } from "i18next"; import { LuCircleX } from "react-icons/lu"; +import SchedulingRadio from "../wizards/scheduling/SchedulingRadio"; /** * This component renders the source page for new events in the new event wizard. @@ -380,9 +381,92 @@ const Schedule = ; } return ( - + <> + + + ); + } + }; + + const renderStreamDeviceOptions = () => { + if (formik.values.location) { + const inputDevice = inputDevices.find( + ({ name }) => name === formik.values.location, + ); + if (!inputDevice) { + return <>; + } + return ( + <> + + + ); + } + }; + + const renderRecordDeviceOptions = () => { + if (formik.values.location) { + const inputDevice = inputDevices.find( + ({ name }) => name === formik.values.location, + ); + if (!inputDevice) { + return <>; + } + return ( + <> + + + ); + } + }; + + const renderLayoutDeviceOptions = () => { + if (formik.values.location) { + const inputDevice = inputDevices.find( + ({ name }) => name === formik.values.location, + ); + if (!inputDevice) { + return <>; + } + return ( + <> + + + ); + } + }; + + const renderCameraPositionDeviceOptions = () => { + if (formik.values.location) { + const inputDevice = inputDevices.find( + ({ name }) => name === formik.values.location, + ); + if (!inputDevice) { + return <>; + } + return ( + <> + + ); } }; @@ -624,6 +708,36 @@ const Schedule = { formik.setFieldValue("location", value); + // Reset location specific fields + const inputDevice = inputDevices.find( + ({ name }) => name === value, + ); + if (inputDevice) { + if (inputDevice.parsedCapabilities.inputs) { + formik.setFieldValue("inputs", []); + } + if (inputDevice.parsedCapabilities.stream) { + if (inputDevice.parsedCapabilities.stream.find(item => item.id === "0")) { + formik.setFieldValue("stream", 0); + } else if (inputDevice.parsedCapabilities.stream.length === 1) { + formik.setFieldValue("stream", inputDevice.parsedCapabilities.stream[0].id); + } else { + formik.setFieldValue("stream", ""); + } + } + if (inputDevice.parsedCapabilities.record) { + if (inputDevice.parsedCapabilities.record.find(item => item.id === "0")) { + formik.setFieldValue("record", 0); + } else if (inputDevice.parsedCapabilities.record.length === 1) { + formik.setFieldValue("record", inputDevice.parsedCapabilities.record[0].id); + } else { + formik.setFieldValue("record", ""); + } + } + if (inputDevice.parsedCapabilities.layout) { + formik.setFieldValue("layout", ""); + } + } }} /> @@ -633,6 +747,30 @@ const Schedule = + + {t("EVENTS.EVENTS.NEW.SOURCE.PLACEHOLDER.STREAM")} + + {renderStreamDeviceOptions()} + + + + {t("EVENTS.EVENTS.NEW.SOURCE.PLACEHOLDER.RECORD")} + + {renderRecordDeviceOptions()} + + + + {t("EVENTS.EVENTS.NEW.SOURCE.PLACEHOLDER.LAYOUT")} + + {renderLayoutDeviceOptions()} + + + + {t("EVENTS.EVENTS.NEW.SOURCE.PLACEHOLDER.CAMERA_POSITION")} + + {renderCameraPositionDeviceOptions()} + + diff --git a/src/components/events/partials/wizards/scheduling/SchedulingInputs.tsx b/src/components/events/partials/wizards/scheduling/SchedulingInputs.tsx index 17bf33c7dc..14cebd858d 100644 --- a/src/components/events/partials/wizards/scheduling/SchedulingInputs.tsx +++ b/src/components/events/partials/wizards/scheduling/SchedulingInputs.tsx @@ -3,8 +3,10 @@ import { Field } from "formik"; import { ParseKeys } from "i18next"; const SchedulingInputs = ({ + name, inputs, }: { + name: string, inputs: { id: string, value: string @@ -18,7 +20,7 @@ const SchedulingInputs = ({ (input, key) => (