From 933c101cbc8b015647bff8e8dcacd019d28d5dc5 Mon Sep 17 00:00:00 2001 From: Toba Ojo Date: Mon, 3 Nov 2025 15:01:13 +0000 Subject: [PATCH 1/5] - refactored to fetch confis based on formats - send configs based on formats --- .../BearerType/BearerTypeCard.tsx | 8 +- .../BearerType/BearerTypeFields.tsx | 95 +++---- .../Channel1-JSON/ChannelCard.tsx | 56 +++- .../Channel1-JSON/ChannelFields.tsx | 241 +++++++++--------- .../SettingForms/SettingForms.tsx | 62 ++++- src/hooks/useCameraBackOfficeOutput.ts | 74 ++++++ src/hooks/useCameraOutput.ts | 65 +---- src/types/types.ts | 3 +- 8 files changed, 353 insertions(+), 251 deletions(-) create mode 100644 src/hooks/useCameraBackOfficeOutput.ts diff --git a/src/components/SettingForms/BearerType/BearerTypeCard.tsx b/src/components/SettingForms/BearerType/BearerTypeCard.tsx index 2e42a60..0435c54 100644 --- a/src/components/SettingForms/BearerType/BearerTypeCard.tsx +++ b/src/components/SettingForms/BearerType/BearerTypeCard.tsx @@ -2,11 +2,15 @@ import Card from "../../UI/Card"; import CardHeader from "../../UI/CardHeader"; import BearerTypeFields from "./BearerTypeFields"; -const BearerTypeCard = () => { +type BearerTypeCardProps = { + options: string[]; +}; + +const BearerTypeCard = ({ options }: BearerTypeCardProps) => { return ( - + ); }; diff --git a/src/components/SettingForms/BearerType/BearerTypeFields.tsx b/src/components/SettingForms/BearerType/BearerTypeFields.tsx index d2207c6..720e2e8 100644 --- a/src/components/SettingForms/BearerType/BearerTypeFields.tsx +++ b/src/components/SettingForms/BearerType/BearerTypeFields.tsx @@ -1,69 +1,46 @@ -import { Field, Form, Formik } from "formik"; +import { Field, Form, useFormikContext } from "formik"; import FormToggle from "../components/FormToggle"; -import { useCameraOutput } from "../../../hooks/useCameraOutput"; -import { cleanArray } from "../../../utils/utils"; import FormGroup from "../components/FormGroup"; -import type { BearerTypeFieldType } from "../../../types/types"; +import type { BearerTypeFieldType, InitialValuesForm } from "../../../types/types"; -export const ValuesComponent = () => { - return null; +type BearerTypeFieldsProps = { + options: string[]; }; -const BearerTypeFields = () => { - const { dispatcherQuery, dispatcherMutation } = useCameraOutput(); - - const format = dispatcherQuery?.data?.propFormat?.value; - const rawOptions = dispatcherQuery?.data?.propFormat?.accepted; - const enabled = dispatcherQuery?.data?.propEnabled?.value; - const verbose = dispatcherQuery?.data?.propVerbose?.value; - const options = cleanArray(rawOptions); - - const initialValues: BearerTypeFieldType = { - format: format ?? "JSON", - enabled: enabled === "true", - verbose: verbose === "true", - }; - - const handleSubmit = async (values: BearerTypeFieldType) => { - await dispatcherMutation.mutateAsync(values); - }; - +const BearerTypeFields = ({ options }: BearerTypeFieldsProps) => { + useFormikContext(); return ( - - {({ isSubmitting }) => ( -
-
- - - - {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 a75cb6e..f983616 100644 --- a/src/components/SettingForms/Channel1-JSON/ChannelCard.tsx +++ b/src/components/SettingForms/Channel1-JSON/ChannelCard.tsx @@ -1,12 +1,62 @@ +import { useFormikContext, type FormikTouched } from "formik"; 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 { 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 mapped = useMemo(() => { + const d = backOfficeQuery.data; + return { + backOfficeURL: d?.propBackofficeURL?.value ?? "", + username: d?.propUsername?.value ?? "", + password: d?.propPassword?.value ?? "", + connectTimeoutSeconds: Number(d?.propConnectTimeoutSeconds?.value), + readTimeoutSeconds: Number(d?.propReadTimeoutSeconds?.value), + }; + }, [backOfficeQuery.data]); + + 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); + }; -const ChannelCard = () => { return ( - - + + ); }; diff --git a/src/components/SettingForms/Channel1-JSON/ChannelFields.tsx b/src/components/SettingForms/Channel1-JSON/ChannelFields.tsx index ef554b4..a2000b0 100644 --- a/src/components/SettingForms/Channel1-JSON/ChannelFields.tsx +++ b/src/components/SettingForms/Channel1-JSON/ChannelFields.tsx @@ -1,43 +1,33 @@ -import { Field, Form, Formik, useFormikContext } from "formik"; +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { Field, Form, 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 { useCameraOutput } from "../../../hooks/useCameraOutput"; -import type { InitialValuesForm, InitialValuesFormErrors } from "../../../types/types"; +import type { BearerTypeFieldType, InitialValuesForm, InitialValuesFormErrors } from "../../../types/types"; import { toast } from "sonner"; +import type { UseMutationResult, UseQueryResult } from "@tanstack/react-query"; -const ChannelFields = () => { +type ChannelFieldsProps = { + touched: FormikTouched; + isSubmitting: boolean; + backOfficeMutation: UseMutationResult; + backOfficeData: UseQueryResult; + handleFormSubmit: (values: BearerTypeFieldType & InitialValuesForm) => Promise; + handleSubmit: () => void; +}; + +const ChannelFields = ({ + touched, + isSubmitting, + backOfficeMutation, + handleFormSubmit, + handleSubmit, +}: ChannelFieldsProps) => { const [showPwd, setShowPwd] = useState(false); - const { backOfficeQuery, backOfficeMutation } = useCameraOutput(); - - const backOfficeURL = backOfficeQuery?.data?.propBackofficeURL?.value; - const username = backOfficeQuery?.data?.propUsername?.value; - const password = backOfficeQuery?.data?.propPassword?.value; - const connectTimeoutSeconds = backOfficeQuery?.data?.propConnectTimeoutSeconds?.value; - const readTimeoutSeconds = backOfficeQuery?.data?.propReadTimeoutSeconds?.value; - - const initialValues: InitialValuesForm = { - backOfficeURL: backOfficeURL ?? "", - username: username ?? "", - password: password ?? "", - connectTimeoutSeconds: Number(connectTimeoutSeconds), - readTimeoutSeconds: Number(readTimeoutSeconds), - }; - - const handleSubmit = async (values: InitialValuesForm) => { - await backOfficeMutation.mutateAsync(values); - }; - - const ValidationToastOnce = () => { - const { submitCount, isValid } = useFormikContext(); - useEffect(() => { - if (submitCount > 0 && !isValid) { - toast.error("Check fields are filled in"); - } - }, [submitCount, isValid]); - return null; - }; + const { submitCount, isValid, values, setErrors, errors } = useFormikContext< + BearerTypeFieldType & InitialValuesForm + >(); const validateValues = (values: InitialValuesForm): InitialValuesFormErrors => { const errors: InitialValuesFormErrors = {}; @@ -66,97 +56,112 @@ const ChannelFields = () => { } 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 ValidationToastOnce = () => { + useEffect(() => { + if (submitCount > 0 && !isValid) { + toast.error("Check fields are filled in"); + } + }, []); + return null; + }; + return ( - - {({ errors, touched, isSubmitting }) => ( -
-
- - + +
+ + - - - - - - - - -
- - setShowPwd((s) => !s)} - icon={showPwd ? faEyeSlash : faEye} - /> -
-
- - - - - - - - - + + + + + + + + +
+ + setShowPwd((s) => !s)} + icon={showPwd ? faEyeSlash : faEye} + />
- - - - )} - +
+ + + + + + + + + +
+ + + ); }; diff --git a/src/components/SettingForms/SettingForms/SettingForms.tsx b/src/components/SettingForms/SettingForms/SettingForms.tsx index 632d076..f0369e5 100644 --- a/src/components/SettingForms/SettingForms/SettingForms.tsx +++ b/src/components/SettingForms/SettingForms/SettingForms.tsx @@ -1,12 +1,66 @@ +import { 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 { cleanArray } from "../../../utils/utils"; +import { useQueryClient } from "@tanstack/react-query"; const SettingForms = () => { + const qc = useQueryClient(); + const { dispatcherQuery, dispatcherMutation } = useCameraOutput(); + + const format = dispatcherQuery?.data?.propFormat?.value; + const rawOptions = dispatcherQuery?.data?.propFormat?.accepted; + const enabled = dispatcherQuery?.data?.propEnabled?.value; + const verbose = dispatcherQuery?.data?.propVerbose?.value; + + const options = cleanArray(rawOptions); + + const handleFormSubmit = async (values: BearerTypeFieldType & InitialValuesForm) => { + console.log(values); + const dispatcherData = { + format: values.format, + enabled: values.enabled, + }; + const result = await dispatcherMutation.mutateAsync(dispatcherData); + + if (result?.id) { + qc.invalidateQueries({ queryKey: ["dispatcher"] }); + qc.invalidateQueries({ queryKey: ["backoffice", values.format] }); + } + }; + + 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/useCameraBackOfficeOutput.ts new file mode 100644 index 0000000..75f5205 --- /dev/null +++ b/src/hooks/useCameraBackOfficeOutput.ts @@ -0,0 +1,74 @@ +import { useMutation, useQuery } from "@tanstack/react-query"; +import { useEffect } from "react"; +import { toast } from "sonner"; +import type { InitialValuesForm } from "../types/types"; +import { CAM_BASE } from "../utils/config"; + +const getBackOfficeConfig = async (format: string) => { + const response = await fetch(`${CAM_BASE}/api/fetch-config?id=Dispatcher-${format?.toLowerCase()}`); + if (!response.ok) throw new Error("Cannot get Back Office configuration"); + return response.json(); +}; + +const updateBackOfficeConfig = async (data: InitialValuesForm) => { + const updateConfigPayload = { + id: `Dispatcher-${data.format.toLowerCase()}`, + fields: [ + { + property: "propBackofficeURL", + value: data.backOfficeURL, + }, + { + property: "propConnectTimeoutSeconds", + value: data.connectTimeoutSeconds, + }, + { + property: "propPassword", + value: data.password, + }, + { + property: "propReadTimeoutSeconds", + value: data.readTimeoutSeconds, + }, + { + property: "propUsername", + value: data.username, + }, + ], + }; + console.log(updateConfigPayload); + const response = await fetch(`${CAM_BASE}/api/update-config?id=Dispatcher-${data.format.toLowerCase()}`, { + method: "POST", + body: JSON.stringify(updateConfigPayload), + }); + if (!response.ok) throw new Error("Cannot update Back Office configuration"); + return response.json(); +}; + +export const useCameraBackOfficeOutput = (format: string) => { + const backOfficeQuery = useQuery({ + queryKey: ["backoffice", format], + queryFn: () => getBackOfficeConfig(format), + enabled: !!format, + }); + + const backOfficeMutation = useMutation({ + mutationKey: ["backOfficeUpdate"], + mutationFn: updateBackOfficeConfig, + onError: (error) => toast.error(error.message), + onSuccess: (data) => { + if (data) { + toast.success("Settings successfully updated"); + } + }, + }); + + useEffect(() => { + if (backOfficeQuery.isError) toast.error(backOfficeQuery.error.message); + }, [backOfficeQuery?.error?.message, backOfficeQuery.isError]); + + return { + backOfficeQuery, + backOfficeMutation, + }; +}; diff --git a/src/hooks/useCameraOutput.ts b/src/hooks/useCameraOutput.ts index 9c79c33..4a13500 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, InitialValuesForm } from "../types/types"; +import type { BearerTypeFieldType } from "../types/types"; const getDispatcherConfig = async () => { const response = await fetch(`${CAM_BASE}/api/fetch-config?id=Dispatcher`); @@ -18,7 +18,6 @@ const updateDispatcherConfig = async (data: BearerTypeFieldType) => { property: "propEnabled", value: data.enabled, }, - // Todo: figure out how to add verbose conditionally { property: "propFormat", value: data.format, @@ -33,57 +32,12 @@ const updateDispatcherConfig = async (data: BearerTypeFieldType) => { return response.json(); }; -const getBackOfficeConfig = async () => { - const response = await fetch(`${CAM_BASE}/api/fetch-config?id=Dispatcher-json`); - if (!response.ok) throw new Error("Cannot get Back Office configuration"); - return response.json(); -}; - -const updateBackOfficeConfig = async (data: InitialValuesForm) => { - const updateConfigPayload = { - id: "Dispatcher-json", - fields: [ - { - property: "propBackofficeURL", - value: data.backOfficeURL, - }, - { - property: "propConnectTimeoutSeconds", - value: data.connectTimeoutSeconds, - }, - { - property: "propPassword", - value: data.password, - }, - { - property: "propReadTimeoutSeconds", - value: data.readTimeoutSeconds, - }, - { - property: "propUsername", - value: data.username, - }, - ], - }; - const response = await fetch(`${CAM_BASE}/api/update-config?id=Dispatcher-json`, { - method: "POST", - body: JSON.stringify(updateConfigPayload), - }); - if (!response.ok) throw new Error("Cannot update Back Office configuration"); - return response.json(); -}; - export const useCameraOutput = () => { const dispatcherQuery = useQuery({ queryKey: ["dispatcher"], queryFn: getDispatcherConfig, }); - const backOfficeQuery = useQuery({ - queryKey: ["backoffice"], - queryFn: getBackOfficeConfig, - }); - const dispatcherMutation = useMutation({ mutationFn: updateDispatcherConfig, mutationKey: ["dispatcherUpdate"], @@ -95,29 +49,12 @@ export const useCameraOutput = () => { }, }); - const backOfficeMutation = useMutation({ - mutationKey: ["backOfficeUpdate"], - mutationFn: updateBackOfficeConfig, - onError: (error) => toast.error(error.message), - onSuccess: (data) => { - if (data) { - toast.success("Settings successfully updated"); - } - }, - }); - useEffect(() => { if (dispatcherQuery.isError) toast.error(dispatcherQuery.error.message); }, [dispatcherQuery?.error?.message, dispatcherQuery.isError]); - useEffect(() => { - if (backOfficeQuery.isError) toast.error(backOfficeQuery.error.message); - }, [backOfficeQuery?.error?.message, backOfficeQuery.isError]); - return { dispatcherQuery, dispatcherMutation, - backOfficeQuery, - backOfficeMutation, }; }; diff --git a/src/types/types.ts b/src/types/types.ts index 45d8c79..ca22586 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -48,10 +48,11 @@ export type CameraSettingErrorValues = Partial Date: Tue, 4 Nov 2025 10:24:06 +0000 Subject: [PATCH 2/5] - updated form to include bof2 constants - refactored code to make more scalable and use one form --- .../BearerType/BearerTypeFields.tsx | 55 ++++---- .../Channel1-JSON/ChannelCard.tsx | 33 ++--- .../Channel1-JSON/ChannelFields.tsx | 132 ++++++++++-------- .../SettingForms/SettingForms.tsx | 107 ++++++++++---- ...OfficeOutput.ts => useBackOfficeConfig.ts} | 23 +-- src/hooks/useCameraOutput.ts | 39 +++++- src/types/types.ts | 7 + 7 files changed, 237 insertions(+), 159 deletions(-) rename src/hooks/{useCameraBackOfficeOutput.ts => useBackOfficeConfig.ts} (95%) 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; From ca625673e94c6195c82d027746a474d3459b6a9e Mon Sep 17 00:00:00 2001 From: Toba Ojo Date: Tue, 4 Nov 2025 11:05:40 +0000 Subject: [PATCH 3/5] - updated to retrieve and set BOF2 data --- .../BearerType/BearerTypeFields.tsx | 1 - .../SettingForms/SettingForms.tsx | 25 +++++++++++++------ src/hooks/useBackOfficeConfig.ts | 2 +- src/hooks/useCameraOutput.ts | 23 +++++++++++++++-- 4 files changed, 39 insertions(+), 12 deletions(-) diff --git a/src/components/SettingForms/BearerType/BearerTypeFields.tsx b/src/components/SettingForms/BearerType/BearerTypeFields.tsx index 4c4fad6..64d7fe9 100644 --- a/src/components/SettingForms/BearerType/BearerTypeFields.tsx +++ b/src/components/SettingForms/BearerType/BearerTypeFields.tsx @@ -30,7 +30,6 @@ const BearerTypeFields = ({ options }: BearerTypeFieldsProps) => {
-
diff --git a/src/components/SettingForms/SettingForms/SettingForms.tsx b/src/components/SettingForms/SettingForms/SettingForms.tsx index e900052..e0517c7 100644 --- a/src/components/SettingForms/SettingForms/SettingForms.tsx +++ b/src/components/SettingForms/SettingForms/SettingForms.tsx @@ -1,7 +1,7 @@ import { Form, Formik } from "formik"; import BearerTypeCard from "../BearerType/BearerTypeCard"; import ChannelCard from "../Channel1-JSON/ChannelCard"; -import { useCameraOutput } from "../../../hooks/useCameraOutput"; +import { useCameraOutput, useGetDispatcherConfig } from "../../../hooks/useCameraOutput"; import type { BearerTypeFieldType, InitialValuesForm, @@ -11,23 +11,29 @@ import type { import { cleanArray } from "../../../utils/utils"; import { useQueryClient } from "@tanstack/react-query"; import { useUpdateBackOfficeConfig } from "../../../hooks/useBackOfficeConfig"; +import { useState } from "react"; const SettingForms = () => { const qc = useQueryClient(); + const [formErrors, setFormErrors] = useState(null); const { dispatcherQuery, dispatcherMutation, backOfficeDispatcherMutation } = useCameraOutput(); const { backOfficeMutation } = useUpdateBackOfficeConfig(); + const { bof2ConstantsQuery } = useGetDispatcherConfig(); const format = dispatcherQuery?.data?.propFormat?.value; const rawOptions = dispatcherQuery?.data?.propFormat?.accepted; const enabled = dispatcherQuery?.data?.propEnabled?.value; - const verbose = dispatcherQuery?.data?.propVerbose?.value; + + const FFID = bof2ConstantsQuery?.data?.propFeedIdentifier?.value; + const SCID = bof2ConstantsQuery?.data?.propSourceIdentifier?.value; + const GPSFormat = bof2ConstantsQuery?.data?.propGpsFormat?.value; + const timestampSource = bof2ConstantsQuery?.data?.propTimeZoneType?.value; const options = cleanArray(rawOptions); const initialValues: BearerTypeFieldType & InitialValuesForm & OptionalBOF2Constants = { format: format ?? "JSON", enabled: enabled === "true", - verbose: verbose === "true", backOfficeURL: "", username: "", password: "", @@ -35,10 +41,10 @@ const SettingForms = () => { readTimeoutSeconds: Number(15), // Bof2 - optional constants - FFID: "", - SCID: "", - timestampSource: "", - GPSFormat: "", + FFID: FFID ?? "", + SCID: SCID ?? "", + timestampSource: timestampSource ?? "", + GPSFormat: GPSFormat ?? "", }; const validateValues = (values: InitialValuesForm): InitialValuesFormErrors => { @@ -47,7 +53,6 @@ const SettingForms = () => { const url = values.backOfficeURL?.trim(); const username = values.username?.trim(); const password = values.password?.trim(); - if (!url) { errors.backOfficeURL = "Required"; } @@ -68,10 +73,14 @@ const SettingForms = () => { } else if (connect < 0) { errors.connectTimeoutSeconds = "Must be ≥ 0"; } + setFormErrors(errors); return errors; }; const handleSubmit = async (values: BearerTypeFieldType & InitialValuesForm & OptionalBOF2Constants) => { + if (formErrors && Object.entries(formErrors).length > 0) { + return; + } const dispatcherData = { format: values.format, enabled: values.enabled, diff --git a/src/hooks/useBackOfficeConfig.ts b/src/hooks/useBackOfficeConfig.ts index 1e04b62..63f936b 100644 --- a/src/hooks/useBackOfficeConfig.ts +++ b/src/hooks/useBackOfficeConfig.ts @@ -67,7 +67,7 @@ export const useUpdateBackOfficeConfig = () => { onError: (error) => toast.error(error.message), onSuccess: (data) => { if (data) { - toast.success("Settings successfully updated"); + toast.success("Settings successfully updated", { id: "dispatchSettings" }); } }, }); diff --git a/src/hooks/useCameraOutput.ts b/src/hooks/useCameraOutput.ts index 0003d26..96c9412 100644 --- a/src/hooks/useCameraOutput.ts +++ b/src/hooks/useCameraOutput.ts @@ -33,7 +33,6 @@ const updateDispatcherConfig = async (data: BearerTypeFieldType) => { }; const updateBackOfficeDispatcher = async (data: OptionalBOF2Constants) => { - console.log(data); const bof2ContantsPayload = { id: "Dispatcher-bof2-constants", fields: [ @@ -63,6 +62,12 @@ const updateBackOfficeDispatcher = async (data: OptionalBOF2Constants) => { return response.json(); }; +const getBof2DispatcherData = async () => { + const response = await fetch(`http://100.118.196.113:8080/api/fetch-config?id=Dispatcher-bof2-constants`); + if (!response.ok) throw new Error("Cannot get BOF2 dispatcher config"); + return response.json(); +}; + export const useCameraOutput = () => { const dispatcherQuery = useQuery({ queryKey: ["dispatcher"], @@ -75,7 +80,7 @@ export const useCameraOutput = () => { onError: (error) => toast.error(error.message), onSuccess: (data) => { if (data) { - toast.success("Settings successfully updated"); + toast.success("Settings successfully updated", { id: "dispatchSettings" }); } }, }); @@ -83,6 +88,11 @@ export const useCameraOutput = () => { const backOfficeDispatcherMutation = useMutation({ mutationKey: ["backofficedDispatcher"], mutationFn: updateBackOfficeDispatcher, + onSuccess: (data) => { + if (data) { + toast.success("Settings successfully updated", { id: "dispatchSettings" }); + } + }, }); useEffect(() => { @@ -95,3 +105,12 @@ export const useCameraOutput = () => { backOfficeDispatcherMutation, }; }; + +export const useGetDispatcherConfig = () => { + const bof2ConstantsQuery = useQuery({ + queryKey: ["getBof2DispatcherData"], + queryFn: getBof2DispatcherData, + }); + + return { bof2ConstantsQuery }; +}; From 705d7c7040234ed9e5af7dd864b17cdcddf5eabf Mon Sep 17 00:00:00 2001 From: Toba Ojo Date: Tue, 4 Nov 2025 11:17:04 +0000 Subject: [PATCH 4/5] - removed other options --- .../BearerType/BearerTypeCard.tsx | 8 +- .../BearerType/BearerTypeFields.tsx | 17 +- .../Channel1-JSON/ChannelFields.tsx | 275 ++++++++++-------- .../SettingForms/SettingForms.tsx | 6 +- 4 files changed, 157 insertions(+), 149 deletions(-) diff --git a/src/components/SettingForms/BearerType/BearerTypeCard.tsx b/src/components/SettingForms/BearerType/BearerTypeCard.tsx index 0435c54..2e42a60 100644 --- a/src/components/SettingForms/BearerType/BearerTypeCard.tsx +++ b/src/components/SettingForms/BearerType/BearerTypeCard.tsx @@ -2,15 +2,11 @@ import Card from "../../UI/Card"; import CardHeader from "../../UI/CardHeader"; import BearerTypeFields from "./BearerTypeFields"; -type BearerTypeCardProps = { - options: string[]; -}; - -const BearerTypeCard = ({ options }: BearerTypeCardProps) => { +const BearerTypeCard = () => { return ( - + ); }; diff --git a/src/components/SettingForms/BearerType/BearerTypeFields.tsx b/src/components/SettingForms/BearerType/BearerTypeFields.tsx index 64d7fe9..c20fa0a 100644 --- a/src/components/SettingForms/BearerType/BearerTypeFields.tsx +++ b/src/components/SettingForms/BearerType/BearerTypeFields.tsx @@ -3,11 +3,7 @@ import FormToggle from "../components/FormToggle"; import FormGroup from "../components/FormGroup"; import type { BearerTypeFieldType, InitialValuesForm } from "../../../types/types"; -type BearerTypeFieldsProps = { - options: string[]; -}; - -const BearerTypeFields = ({ options }: BearerTypeFieldsProps) => { +const BearerTypeFields = () => { useFormikContext(); return ( @@ -20,11 +16,12 @@ const BearerTypeFields = ({ options }: BearerTypeFieldsProps) => { id="format" className="p-2 border border-gray-400 rounded-lg text-white bg-[#253445] w-full md:w-60" > - {options?.map((option: string) => ( - - ))} + + diff --git a/src/components/SettingForms/Channel1-JSON/ChannelFields.tsx b/src/components/SettingForms/Channel1-JSON/ChannelFields.tsx index f2f191e..1c8dbaa 100644 --- a/src/components/SettingForms/Channel1-JSON/ChannelFields.tsx +++ b/src/components/SettingForms/Channel1-JSON/ChannelFields.tsx @@ -31,146 +31,165 @@ const ChannelFields = ({ touched, isSubmitting, format }: ChannelFieldsProps) => return ( <> -
- - - - - - - - - - - -
- - setShowPwd((s) => !s)} - icon={showPwd ? faEyeSlash : faEye} - /> -
-
- - - - - - - - - - {format?.toLowerCase() === "bof2" && ( - <> -
-

{values.format} Constants

+ {format?.toLowerCase() !== "bof2" && format?.toLowerCase() !== "json" ? ( + <> +
+
+ Format coming soon
+ +

+ Output configuration currently supports JSON or{" "} + BOF2.
More formats will be added in future + updates. +

+
+ + ) : ( + <> +
- + + + + + + + + + +
+ + setShowPwd((s) => !s)} + icon={showPwd ? faEyeSlash : faEye} + /> +
+
+ + + + + + + + - - - - + {format?.toLowerCase() === "bof2" && ( + <> +
+

{values.format} Constants

+
+ + + + - - - - - - - - - - - - - - - - - - )} -
+ + + + - - + + + + + + + + + + + + + + + + + + )} +
+ + + + + )} ); }; diff --git a/src/components/SettingForms/SettingForms/SettingForms.tsx b/src/components/SettingForms/SettingForms/SettingForms.tsx index e0517c7..54f3534 100644 --- a/src/components/SettingForms/SettingForms/SettingForms.tsx +++ b/src/components/SettingForms/SettingForms/SettingForms.tsx @@ -8,7 +8,6 @@ import type { InitialValuesFormErrors, OptionalBOF2Constants, } from "../../../types/types"; -import { cleanArray } from "../../../utils/utils"; import { useQueryClient } from "@tanstack/react-query"; import { useUpdateBackOfficeConfig } from "../../../hooks/useBackOfficeConfig"; import { useState } from "react"; @@ -21,7 +20,6 @@ const SettingForms = () => { const { bof2ConstantsQuery } = useGetDispatcherConfig(); const format = dispatcherQuery?.data?.propFormat?.value; - const rawOptions = dispatcherQuery?.data?.propFormat?.accepted; const enabled = dispatcherQuery?.data?.propEnabled?.value; const FFID = bof2ConstantsQuery?.data?.propFeedIdentifier?.value; @@ -29,8 +27,6 @@ const SettingForms = () => { const GPSFormat = bof2ConstantsQuery?.data?.propGpsFormat?.value; const timestampSource = bof2ConstantsQuery?.data?.propTimeZoneType?.value; - const options = cleanArray(rawOptions); - const initialValues: BearerTypeFieldType & InitialValuesForm & OptionalBOF2Constants = { format: format ?? "JSON", enabled: enabled === "true", @@ -109,7 +105,7 @@ const SettingForms = () => { {({ isSubmitting, touched }) => (
- +
From f47459d116099fba296dd12cd2c151127f99c7b3 Mon Sep 17 00:00:00 2001 From: Toba Ojo Date: Tue, 4 Nov 2025 11:31:37 +0000 Subject: [PATCH 5/5] - added validation endpoint --- .../SettingForms/SettingForms.tsx | 18 +++++--- src/hooks/useFormValidate.ts | 46 +++++++++++++++++++ 2 files changed, 57 insertions(+), 7 deletions(-) create mode 100644 src/hooks/useFormValidate.ts diff --git a/src/components/SettingForms/SettingForms/SettingForms.tsx b/src/components/SettingForms/SettingForms/SettingForms.tsx index 54f3534..f89b538 100644 --- a/src/components/SettingForms/SettingForms/SettingForms.tsx +++ b/src/components/SettingForms/SettingForms/SettingForms.tsx @@ -10,14 +10,14 @@ import type { } from "../../../types/types"; import { useQueryClient } from "@tanstack/react-query"; import { useUpdateBackOfficeConfig } from "../../../hooks/useBackOfficeConfig"; -import { useState } from "react"; +import { useFormVaidate } from "../../../hooks/useFormValidate"; const SettingForms = () => { const qc = useQueryClient(); - const [formErrors, setFormErrors] = useState(null); const { dispatcherQuery, dispatcherMutation, backOfficeDispatcherMutation } = useCameraOutput(); const { backOfficeMutation } = useUpdateBackOfficeConfig(); const { bof2ConstantsQuery } = useGetDispatcherConfig(); + const { validateMutation } = useFormVaidate(); const format = dispatcherQuery?.data?.propFormat?.value; const enabled = dispatcherQuery?.data?.propEnabled?.value; @@ -69,14 +69,13 @@ const SettingForms = () => { } else if (connect < 0) { errors.connectTimeoutSeconds = "Must be ≥ 0"; } - setFormErrors(errors); return errors; }; const handleSubmit = async (values: BearerTypeFieldType & InitialValuesForm & OptionalBOF2Constants) => { - if (formErrors && Object.entries(formErrors).length > 0) { - return; - } + // if (formErrors && Object.entries(formErrors).length > 0) { + // return; + // } const dispatcherData = { format: values.format, enabled: values.enabled, @@ -86,7 +85,12 @@ const SettingForms = () => { if (result?.id) { qc.invalidateQueries({ queryKey: ["dispatcher"] }); qc.invalidateQueries({ queryKey: ["backoffice", values.format] }); - await backOfficeMutation.mutateAsync(values); + const validResponse = await validateMutation.mutateAsync(values); + if (validResponse?.reason === "OK") { + await backOfficeMutation.mutateAsync(values); + } else { + return; + } } if (values.format.toLowerCase() === "bof2") { diff --git a/src/hooks/useFormValidate.ts b/src/hooks/useFormValidate.ts new file mode 100644 index 0000000..036d34c --- /dev/null +++ b/src/hooks/useFormValidate.ts @@ -0,0 +1,46 @@ +import { useMutation } from "@tanstack/react-query"; +import { CAM_BASE } from "../utils/config"; +import type { InitialValuesForm } from "../types/types"; + +const sendToValidate = async (data: InitialValuesForm) => { + const updateConfigPayload = { + id: `Dispatcher-${data.format.toLowerCase()}`, + fields: [ + { + property: "propBackofficeURL", + value: data.backOfficeURL, + }, + { + property: "propConnectTimeoutSeconds", + value: data.connectTimeoutSeconds, + }, + { + property: "propPassword", + value: data.password, + }, + { + property: "propReadTimeoutSeconds", + value: data.readTimeoutSeconds, + }, + { + property: "propUsername", + value: data.username, + }, + ], + }; + const response = await fetch(`${CAM_BASE}/api/update-config-isvalid`, { + method: "post", + body: JSON.stringify(updateConfigPayload), + }); + if (!response.ok) throw new Error("Cannot send to validate"); + return response.json(); +}; + +export const useFormVaidate = () => { + const validateMutation = useMutation({ + mutationKey: ["sendToValidate"], + mutationFn: sendToValidate, + }); + + return { validateMutation }; +};