diff --git a/src/components/SettingForms/BearerType/BearerTypeFields.tsx b/src/components/SettingForms/BearerType/BearerTypeFields.tsx index 720e2e8..4c4fad6 100644 --- a/src/components/SettingForms/BearerType/BearerTypeFields.tsx +++ b/src/components/SettingForms/BearerType/BearerTypeFields.tsx @@ -1,4 +1,4 @@ -import { Field, Form, useFormikContext } from "formik"; +import { Field, useFormikContext } from "formik"; import FormToggle from "../components/FormToggle"; import FormGroup from "../components/FormGroup"; import type { BearerTypeFieldType, InitialValuesForm } from "../../../types/types"; @@ -9,38 +9,31 @@ type BearerTypeFieldsProps = { const BearerTypeFields = ({ options }: BearerTypeFieldsProps) => { useFormikContext(); + return ( -
-
- - - - {options?.map((option: string) => ( - - ))} - - - -
- - -
-
- {/* */} -
-
+ {options?.map((option: string) => ( + + ))} + + + +
+ + +
+
+ ); }; diff --git a/src/components/SettingForms/Channel1-JSON/ChannelCard.tsx b/src/components/SettingForms/Channel1-JSON/ChannelCard.tsx index f983616..3311e09 100644 --- a/src/components/SettingForms/Channel1-JSON/ChannelCard.tsx +++ b/src/components/SettingForms/Channel1-JSON/ChannelCard.tsx @@ -3,18 +3,17 @@ import Card from "../../UI/Card"; import CardHeader from "../../UI/CardHeader"; import ChannelFields from "./ChannelFields"; import type { BearerTypeFieldType, InitialValuesForm } from "../../../types/types"; -import { useCameraBackOfficeOutput } from "../../../hooks/useCameraBackOfficeOutput"; +import { useCameraBackOfficeOutput } from "../../../hooks/useBackOfficeConfig"; import { useEffect, useMemo } from "react"; type ChannelCardProps = { touched: FormikTouched; isSubmitting: boolean; - handleFormSubmit: (values: BearerTypeFieldType & InitialValuesForm) => Promise; }; -const ChannelCard = ({ touched, isSubmitting, handleFormSubmit }: ChannelCardProps) => { - const { values, resetForm } = useFormikContext(); - const { backOfficeQuery, backOfficeMutation } = useCameraBackOfficeOutput(values?.format); +const ChannelCard = ({ touched, isSubmitting }: ChannelCardProps) => { + const { values, setFieldValue } = useFormikContext(); + const { backOfficeQuery } = useCameraBackOfficeOutput(values?.format); const mapped = useMemo(() => { const d = backOfficeQuery.data; @@ -29,22 +28,10 @@ const ChannelCard = ({ touched, isSubmitting, handleFormSubmit }: ChannelCardPro useEffect(() => { if (!backOfficeQuery.isSuccess) return; - resetForm({ values: { ...values, ...mapped } }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [backOfficeQuery.isSuccess, mapped]); - - console.log(backOfficeQuery.isFetching); - const handleSubmit = async () => { - const backOfficeData = { - format: values.format, - backOfficeURL: values.backOfficeURL, - connectTimeoutSeconds: values.connectTimeoutSeconds, - password: values.password, - readTimeoutSeconds: values.readTimeoutSeconds, - username: values.username, - }; - await backOfficeMutation.mutateAsync(backOfficeData); - }; + for (const [key, value] of Object.entries(mapped)) { + setFieldValue(key, value); + } + }, [backOfficeQuery.isSuccess, mapped, setFieldValue]); return ( @@ -53,9 +40,7 @@ const ChannelCard = ({ touched, isSubmitting, handleFormSubmit }: ChannelCardPro touched={touched} isSubmitting={isSubmitting} backOfficeData={backOfficeQuery} - backOfficeMutation={backOfficeMutation} - handleFormSubmit={handleFormSubmit} - handleSubmit={handleSubmit} + format={values?.format} /> ); diff --git a/src/components/SettingForms/Channel1-JSON/ChannelFields.tsx b/src/components/SettingForms/Channel1-JSON/ChannelFields.tsx index a2000b0..f2f191e 100644 --- a/src/components/SettingForms/Channel1-JSON/ChannelFields.tsx +++ b/src/components/SettingForms/Channel1-JSON/ChannelFields.tsx @@ -1,73 +1,24 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { Field, Form, useFormikContext, type FormikTouched } from "formik"; +import { Field, useFormikContext, type FormikTouched } from "formik"; import FormGroup from "../components/FormGroup"; import { useEffect, useState } from "react"; import { faEyeSlash, faEye } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import type { BearerTypeFieldType, InitialValuesForm, InitialValuesFormErrors } from "../../../types/types"; +import type { BearerTypeFieldType, InitialValuesForm } from "../../../types/types"; import { toast } from "sonner"; -import type { UseMutationResult, UseQueryResult } from "@tanstack/react-query"; +import type { UseQueryResult } from "@tanstack/react-query"; type ChannelFieldsProps = { touched: FormikTouched; isSubmitting: boolean; - backOfficeMutation: UseMutationResult; + backOfficeData: UseQueryResult; - handleFormSubmit: (values: BearerTypeFieldType & InitialValuesForm) => Promise; - handleSubmit: () => void; + format?: string; }; -const ChannelFields = ({ - touched, - isSubmitting, - backOfficeMutation, - handleFormSubmit, - handleSubmit, -}: ChannelFieldsProps) => { +const ChannelFields = ({ touched, isSubmitting, format }: ChannelFieldsProps) => { const [showPwd, setShowPwd] = useState(false); - const { submitCount, isValid, values, setErrors, errors } = useFormikContext< - BearerTypeFieldType & InitialValuesForm - >(); - - const validateValues = (values: InitialValuesForm): InitialValuesFormErrors => { - const errors: InitialValuesFormErrors = {}; - - const url = values.backOfficeURL?.trim(); - const username = values.username?.trim(); - const password = values.password?.trim(); - - if (!url) { - errors.backOfficeURL = "Required"; - } - - if (!username) errors.username = "Required"; - if (!password) errors.password = "Required"; - - const read = Number(values.readTimeoutSeconds); - if (!Number.isFinite(read)) { - errors.readTimeoutSeconds = "Must be a number"; - } else if (read < 0) { - errors.readTimeoutSeconds = "Must be ≥ 0"; - } - - const connect = Number(values.connectTimeoutSeconds); - if (!Number.isFinite(connect)) { - errors.connectTimeoutSeconds = "Must be a number"; - } else if (connect < 0) { - errors.connectTimeoutSeconds = "Must be ≥ 0"; - } - return errors; - }; - - const onSubmit = (values: BearerTypeFieldType & InitialValuesForm) => { - const validationErrors = validateValues(values); - if (Object.values(validationErrors).length > 0) { - setErrors(validationErrors); - return; - } - handleFormSubmit(values); - handleSubmit(); - }; + const { submitCount, isValid, values, errors } = useFormikContext(); const ValidationToastOnce = () => { useEffect(() => { @@ -79,7 +30,7 @@ const ChannelFields = ({ }; return ( -
+ <>
+ {format?.toLowerCase() === "bof2" && ( + <> +
+

{values.format} Constants

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + )}
+ - + ); }; diff --git a/src/components/SettingForms/SettingForms/SettingForms.tsx b/src/components/SettingForms/SettingForms/SettingForms.tsx index f0369e5..e900052 100644 --- a/src/components/SettingForms/SettingForms/SettingForms.tsx +++ b/src/components/SettingForms/SettingForms/SettingForms.tsx @@ -1,14 +1,21 @@ -import { Formik } from "formik"; +import { Form, Formik } from "formik"; import BearerTypeCard from "../BearerType/BearerTypeCard"; import ChannelCard from "../Channel1-JSON/ChannelCard"; import { useCameraOutput } from "../../../hooks/useCameraOutput"; -import type { BearerTypeFieldType, InitialValuesForm } from "../../../types/types"; +import type { + BearerTypeFieldType, + InitialValuesForm, + InitialValuesFormErrors, + OptionalBOF2Constants, +} from "../../../types/types"; import { cleanArray } from "../../../utils/utils"; import { useQueryClient } from "@tanstack/react-query"; +import { useUpdateBackOfficeConfig } from "../../../hooks/useBackOfficeConfig"; const SettingForms = () => { const qc = useQueryClient(); - const { dispatcherQuery, dispatcherMutation } = useCameraOutput(); + const { dispatcherQuery, dispatcherMutation, backOfficeDispatcherMutation } = useCameraOutput(); + const { backOfficeMutation } = useUpdateBackOfficeConfig(); const format = dispatcherQuery?.data?.propFormat?.value; const rawOptions = dispatcherQuery?.data?.propFormat?.accepted; @@ -17,8 +24,54 @@ const SettingForms = () => { const options = cleanArray(rawOptions); - const handleFormSubmit = async (values: BearerTypeFieldType & InitialValuesForm) => { - console.log(values); + const initialValues: BearerTypeFieldType & InitialValuesForm & OptionalBOF2Constants = { + format: format ?? "JSON", + enabled: enabled === "true", + verbose: verbose === "true", + backOfficeURL: "", + username: "", + password: "", + connectTimeoutSeconds: Number(5), + readTimeoutSeconds: Number(15), + + // Bof2 - optional constants + FFID: "", + SCID: "", + timestampSource: "", + GPSFormat: "", + }; + + const validateValues = (values: InitialValuesForm): InitialValuesFormErrors => { + const errors: InitialValuesFormErrors = {}; + + const url = values.backOfficeURL?.trim(); + const username = values.username?.trim(); + const password = values.password?.trim(); + + if (!url) { + errors.backOfficeURL = "Required"; + } + + if (!username) errors.username = "Required"; + if (!password) errors.password = "Required"; + + const read = Number(values.readTimeoutSeconds); + if (!Number.isFinite(read)) { + errors.readTimeoutSeconds = "Must be a number"; + } else if (read < 0) { + errors.readTimeoutSeconds = "Must be ≥ 0"; + } + + const connect = Number(values.connectTimeoutSeconds); + if (!Number.isFinite(connect)) { + errors.connectTimeoutSeconds = "Must be a number"; + } else if (connect < 0) { + errors.connectTimeoutSeconds = "Must be ≥ 0"; + } + return errors; + }; + + const handleSubmit = async (values: BearerTypeFieldType & InitialValuesForm & OptionalBOF2Constants) => { const dispatcherData = { format: values.format, enabled: values.enabled, @@ -28,37 +81,29 @@ const SettingForms = () => { if (result?.id) { qc.invalidateQueries({ queryKey: ["dispatcher"] }); qc.invalidateQueries({ queryKey: ["backoffice", values.format] }); + await backOfficeMutation.mutateAsync(values); + } + + if (values.format.toLowerCase() === "bof2") { + const bof2ConstantsData: OptionalBOF2Constants = { + FFID: values.FFID, + SCID: values.SCID, + timestampSource: values.timestampSource, + GPSFormat: values.GPSFormat, + }; + await backOfficeDispatcherMutation.mutateAsync(bof2ConstantsData); } }; - const initialValues: BearerTypeFieldType & InitialValuesForm = { - format: format ?? "JSON", - enabled: enabled === "true", - verbose: verbose === "true", - backOfficeURL: "", - username: "", - password: "", - connectTimeoutSeconds: Number(5), - readTimeoutSeconds: Number(15), - }; - - const handleSubmit = async (values: BearerTypeFieldType) => { - console.log(values); - // await dispatcherMutation.mutateAsync(values); - }; - return ( - + {({ isSubmitting, touched }) => ( -
- - -
+
+
+ + +
+
)}
); diff --git a/src/hooks/useCameraBackOfficeOutput.ts b/src/hooks/useBackOfficeConfig.ts similarity index 95% rename from src/hooks/useCameraBackOfficeOutput.ts rename to src/hooks/useBackOfficeConfig.ts index 75f5205..1e04b62 100644 --- a/src/hooks/useCameraBackOfficeOutput.ts +++ b/src/hooks/useBackOfficeConfig.ts @@ -36,8 +36,7 @@ const updateBackOfficeConfig = async (data: InitialValuesForm) => { }, ], }; - console.log(updateConfigPayload); - const response = await fetch(`${CAM_BASE}/api/update-config?id=Dispatcher-${data.format.toLowerCase()}`, { + const response = await fetch(`${CAM_BASE}/api/update-config?id=Dispatcher`, { method: "POST", body: JSON.stringify(updateConfigPayload), }); @@ -52,6 +51,16 @@ export const useCameraBackOfficeOutput = (format: string) => { enabled: !!format, }); + useEffect(() => { + if (backOfficeQuery.isError) toast.error(backOfficeQuery.error.message); + }, [backOfficeQuery?.error?.message, backOfficeQuery.isError]); + + return { + backOfficeQuery, + }; +}; + +export const useUpdateBackOfficeConfig = () => { const backOfficeMutation = useMutation({ mutationKey: ["backOfficeUpdate"], mutationFn: updateBackOfficeConfig, @@ -62,13 +71,5 @@ export const useCameraBackOfficeOutput = (format: string) => { } }, }); - - useEffect(() => { - if (backOfficeQuery.isError) toast.error(backOfficeQuery.error.message); - }, [backOfficeQuery?.error?.message, backOfficeQuery.isError]); - - return { - backOfficeQuery, - backOfficeMutation, - }; + return { backOfficeMutation }; }; diff --git a/src/hooks/useCameraOutput.ts b/src/hooks/useCameraOutput.ts index 4a13500..0003d26 100644 --- a/src/hooks/useCameraOutput.ts +++ b/src/hooks/useCameraOutput.ts @@ -2,7 +2,7 @@ import { useMutation, useQuery } from "@tanstack/react-query"; import { CAM_BASE } from "../utils/config"; import { useEffect } from "react"; import { toast } from "sonner"; -import type { BearerTypeFieldType } from "../types/types"; +import type { BearerTypeFieldType, OptionalBOF2Constants } from "../types/types"; const getDispatcherConfig = async () => { const response = await fetch(`${CAM_BASE}/api/fetch-config?id=Dispatcher`); @@ -32,6 +32,37 @@ const updateDispatcherConfig = async (data: BearerTypeFieldType) => { return response.json(); }; +const updateBackOfficeDispatcher = async (data: OptionalBOF2Constants) => { + console.log(data); + const bof2ContantsPayload = { + id: "Dispatcher-bof2-constants", + fields: [ + { + property: "propFeedIdentifier", + value: data?.FFID, + }, + { + property: "propSourceIdentifier", + value: data?.SCID, + }, + { + property: "propTimeZoneType", + value: data?.timestampSource, + }, + { + property: "propGpsFormat", + value: data?.GPSFormat, + }, + ], + }; + const response = await fetch(`${CAM_BASE}/api/update-config?id=Dispatcher-bof2-constants`, { + method: "POST", + body: JSON.stringify(bof2ContantsPayload), + }); + if (!response.ok) throw new Error("Cannot update dispatcher configuration"); + return response.json(); +}; + export const useCameraOutput = () => { const dispatcherQuery = useQuery({ queryKey: ["dispatcher"], @@ -49,6 +80,11 @@ export const useCameraOutput = () => { }, }); + const backOfficeDispatcherMutation = useMutation({ + mutationKey: ["backofficedDispatcher"], + mutationFn: updateBackOfficeDispatcher, + }); + useEffect(() => { if (dispatcherQuery.isError) toast.error(dispatcherQuery.error.message); }, [dispatcherQuery?.error?.message, dispatcherQuery.isError]); @@ -56,5 +92,6 @@ export const useCameraOutput = () => { return { dispatcherQuery, dispatcherMutation, + backOfficeDispatcherMutation, }; }; diff --git a/src/types/types.ts b/src/types/types.ts index ca22586..9229e44 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -68,6 +68,13 @@ export type InitialValuesFormErrors = { readTimeoutSeconds?: string; }; +export type OptionalBOF2Constants = { + FFID?: ""; + SCID?: ""; + timestampSource?: ""; + GPSFormat?: ""; +}; + export type NPEDFieldType = { frontId: string; username: string | undefined;