diff --git a/pwa/src/apiService/resources/synchronization.ts b/pwa/src/apiService/resources/synchronization.ts index 9358f341..a89bfcbb 100644 --- a/pwa/src/apiService/resources/synchronization.ts +++ b/pwa/src/apiService/resources/synchronization.ts @@ -1,66 +1,60 @@ -import { AxiosInstance } from "axios"; -import { paramsToQueryParams } from "../../services/paramsToQueryParams"; -import { TSendFunction } from "../apiService"; - -export default class Synchroniation { - private _instance: AxiosInstance; - private _send: TSendFunction; - - constructor(instance: AxiosInstance, send: TSendFunction) { - this._instance = instance; - this._send = send; - } - - public getAll = async (): Promise => { - const { data } = await this._send(this._instance, "GET", "/admin/synchronizations"); - - return data; - }; - - public getOne = async (id: string): Promise => { - const { data } = await this._send(this._instance, "GET", `/admin/synchronizations/${id}`); - - return data; - }; - - public delete = async (variables: { id: string }): Promise => { - const { id } = variables; - - const { data } = await this._send(this._instance, "DELETE", `/admin/synchronizations/${id}`); - return data; - }; - - public createOrUpdate = async (variables: { - payload: any; - objectId: string; - sourceId: string; - syncId?: string; - }): Promise => { - const { payload, sourceId, objectId, syncId } = variables; - - const params = { - action: payload.action && payload.action.value, - endpoint: payload.endpoint, - externalId: payload.externalId, - sourceId: sourceId, - }; - - const _payload = { - ...payload, - action: payload.action && `/admin/actions/${payload.action.value}`, - sourceId: payload.source.value, - }; - - if (syncId) { - const { data } = await this._send(this._instance, "PUT", `/admin/synchronizations/${syncId}`, _payload); - return data; - } - - const { data } = await this._send( - this._instance, - "POST", - `/admin/object_entities/${objectId}/sync/${sourceId}${paramsToQueryParams(params, true)}`, - ); - return data; - }; -} +import { AxiosInstance } from "axios"; +import { paramsToQueryParams } from "../../services/paramsToQueryParams"; +import { TSendFunction } from "../apiService"; + +export default class Synchroniation { + private _instance: AxiosInstance; + private _send: TSendFunction; + + constructor(instance: AxiosInstance, send: TSendFunction) { + this._instance = instance; + this._send = send; + } + + public getAll = async (): Promise => { + const { data } = await this._send(this._instance, "GET", "/admin/synchronizations"); + + return data; + }; + + public getOne = async (id: string): Promise => { + const { data } = await this._send(this._instance, "GET", `/admin/synchronizations/${id}`); + + return data; + }; + + public delete = async (variables: { id: string }): Promise => { + const { id } = variables; + + const { data } = await this._send(this._instance, "DELETE", `/admin/synchronizations/${id}`); + return data; + }; + + public createOrUpdate = async (variables: { payload: any; objectId: string; syncId?: string }): Promise => { + const { payload, objectId, syncId } = variables; + + const _payload = { + ...payload, + entity: payload.entity && `/admin/entities/${payload.entity}`, + object: objectId, + action: payload.action && `/admin/actions/${payload.action.value}`, + gateway: payload.source && `/admin/gateways/${payload.source.value}`, + }; + + delete _payload.source; + + if (syncId) { + const { data } = await this._send(this._instance, "PUT", `/admin/synchronizations/${syncId}`, _payload, { + loading: "Updating synchronization...", + success: "Synchronization successfully updated.", + }); + return data; + } + + const { data } = await this._send(this._instance, "POST", "/admin/synchronizations", _payload, { + loading: "Creating synchronization...", + success: "Synchronization successfully created.", + }); + return data; + }; +} diff --git a/pwa/src/context/isLoading.ts b/pwa/src/context/isLoading.ts index eab46044..9c59dded 100644 --- a/pwa/src/context/isLoading.ts +++ b/pwa/src/context/isLoading.ts @@ -15,6 +15,7 @@ export interface IIsLoadingContext { organizationForm?: boolean; mappingForm?: boolean; loginForm?: boolean; + syncForm?: boolean; } export const defaultIsLoadingContext: IIsLoadingContext = {}; diff --git a/pwa/src/hooks/synchronization.ts b/pwa/src/hooks/synchronization.ts index d99d077e..206a09df 100644 --- a/pwa/src/hooks/synchronization.ts +++ b/pwa/src/hooks/synchronization.ts @@ -28,12 +28,13 @@ export const useSync = (queryClient: QueryClient) => { enabled: !!syncId && !isDeleted(syncId), }); - const remove = () => + const remove = (objectId?: string) => useMutation(API.Synchroniation.delete, { onMutate: ({ id }) => addDeletedItem(id), onSuccess: async (_, variables) => { deleteItem(queryClient, "object", variables.id); queryClient.invalidateQueries(["synchronizations"]); + objectId && navigate(`/objects/${objectId}`); }, onError: (error, { id }) => { removeDeletedItem(id); @@ -48,8 +49,6 @@ export const useSync = (queryClient: QueryClient) => { useMutation(API.Synchroniation.createOrUpdate, { onSuccess: async (newSync) => { if (syncId) { - toast.success("Succesfully updated synchroniation"); - updateItem(queryClient, "synchronizations", newSync); navigate(`/objects/${objectId}`); } diff --git a/pwa/src/pages/objects/[objectId]/[syncId]/SyncDetailPage.tsx b/pwa/src/pages/objects/[objectId]/[syncId]/SyncDetailPage.tsx index ca4c1081..f23111c0 100644 --- a/pwa/src/pages/objects/[objectId]/[syncId]/SyncDetailPage.tsx +++ b/pwa/src/pages/objects/[objectId]/[syncId]/SyncDetailPage.tsx @@ -2,15 +2,17 @@ import * as React from "react"; import { PageProps } from "gatsby"; import { DashboardTemplate } from "../../../../templates/dashboard/DashboardTemplate"; import { SyncDetailTemplate } from "../../../../templates/syncDetailTemplate/SyncDetailTemplate"; -import { CreateSyncFormTemplate } from "../../../../templates/templateParts/syncForm/CreateSyncFormTemplate"; +import { CreateSyncTemplate } from "../../../../templates/templateParts/syncForm/CreateSyncTemplate"; -const SyncDetailPage: React.FC = (props: PageProps) => ( - - {props.params.syncId === "new" && } - {props.params.syncId !== "new" && ( - - )} - -); +const SyncDetailPage: React.FC = (props: PageProps) => { + const syncId = props.params.syncId === "new" ? null : props.params.syncId; + + return ( + + {!syncId && } + {syncId && } + + ); +}; export default SyncDetailPage; diff --git a/pwa/src/templates/syncDetailTemplate/SyncDetailTemplate.tsx b/pwa/src/templates/syncDetailTemplate/SyncDetailTemplate.tsx index 84ac1b17..fa2e45d5 100644 --- a/pwa/src/templates/syncDetailTemplate/SyncDetailTemplate.tsx +++ b/pwa/src/templates/syncDetailTemplate/SyncDetailTemplate.tsx @@ -5,11 +5,13 @@ import { useTranslation } from "react-i18next"; import { useQueryClient } from "react-query"; import { Container } from "@conduction/components"; import Skeleton from "react-loading-skeleton"; -import { EditSyncFormTemplate } from "../templateParts/syncForm/EditSyncFormTemplate"; +import { SyncFormTemplate, formId } from "../templateParts/syncForm/SyncFormTemplate"; import { useSync } from "../../hooks/synchronization"; import { navigate } from "gatsby"; import { faArrowLeft } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { FormHeaderTemplate } from "../templateParts/formHeader/FormHeaderTemplate"; +import { useIsLoadingContext } from "../../context/isLoading"; interface SyncDetailTemplateProps { syncId: string; @@ -18,10 +20,16 @@ interface SyncDetailTemplateProps { export const SyncDetailTemplate: React.FC = ({ syncId, objectId }) => { const { t } = useTranslation(); + const { setIsLoading, isLoading } = useIsLoadingContext(); const queryClient = useQueryClient(); const _useSync = useSync(queryClient); const getSynchronization = _useSync.getOne(syncId); + const deleteSync = _useSync.remove(objectId); + + React.useEffect(() => { + setIsLoading({ syncForm: deleteSync.isLoading }); + }, [deleteSync.isLoading]); return ( @@ -33,7 +41,16 @@ export const SyncDetailTemplate: React.FC = ({ syncId, {getSynchronization.isError && "Error..."} {getSynchronization.isSuccess && ( - + <> + deleteSync.mutateAsync({ id: syncId })} + showTitleTooltip + /> + + )} {getSynchronization.isLoading && } diff --git a/pwa/src/templates/templateParts/objectsFormTemplate/CreateObjectFormTemplate.tsx b/pwa/src/templates/templateParts/objectsFormTemplate/CreateObjectFormTemplate.tsx index 0680daad..667046be 100644 --- a/pwa/src/templates/templateParts/objectsFormTemplate/CreateObjectFormTemplate.tsx +++ b/pwa/src/templates/templateParts/objectsFormTemplate/CreateObjectFormTemplate.tsx @@ -76,7 +76,7 @@ export const CreateObjectFormTemplate: React.FC = onSuccess: (newObject) => { switch (afterSuccessfulFormSubmit) { case "save": - navigate(`/objects/${newObject._id}`); + navigate(`/objects/${newObject.id ?? newObject._id}`); break; case "saveAndClose": diff --git a/pwa/src/templates/templateParts/syncForm/CreateSyncFormTemplate.tsx b/pwa/src/templates/templateParts/syncForm/CreateSyncFormTemplate.tsx deleted file mode 100644 index 8f674c02..00000000 --- a/pwa/src/templates/templateParts/syncForm/CreateSyncFormTemplate.tsx +++ /dev/null @@ -1,125 +0,0 @@ -import * as React from "react"; -import * as styles from "./SyncFormTemplate.module.css"; -import { useForm } from "react-hook-form"; -import FormField, { FormFieldInput, FormFieldLabel } from "@gemeente-denhaag/form-field"; -import { Alert, Heading1 } from "@gemeente-denhaag/components-react"; -import { useTranslation } from "react-i18next"; -import { InputText, SelectSingle } from "@conduction/components"; -import { faSave } from "@fortawesome/free-solid-svg-icons"; -import { useQueryClient } from "react-query"; -import { useSource } from "../../../hooks/source"; -import Skeleton from "react-loading-skeleton"; -import { useAction } from "../../../hooks/action"; -import { useSync } from "../../../hooks/synchronization"; -import { Button } from "../../../components/button/Button"; -import { enrichValidation } from "../../../services/enrichReactHookFormValidation"; - -interface CreateSyncFormTemplateProps { - objectId: string; -} - -export const CreateSyncFormTemplate: React.FC = ({ objectId }) => { - const { t } = useTranslation(); - const [loading, setLoading] = React.useState(false); - const [formError, setFormError] = React.useState(""); - - const queryClient = useQueryClient(); - const _useSync = useSync(queryClient); - const syncObject = _useSync.createOrEdit(objectId); - - const _useSource = useSource(queryClient); - const getSources = _useSource.getAll(); - - const _useAction = useAction(queryClient); - const getActions = _useAction.getAll(); - - const syncActions = - getActions && - getActions.data?.filter((action) => action.class === "App\\ActionHandler\\SynchronizationItemHandler"); - - const { - register, - handleSubmit, - control, - formState: { errors }, - } = useForm(); - - const onSubmit = (data: any): void => { - const payload = { - ...data, - }; - const sourceId = data.source.value; - - syncObject.mutate({ payload, objectId, sourceId }); - }; - - return ( -
-
-
- {t("Create Synchronization")} - -
-
-
- {formError && } -
-
- - - {t("Select a source")} - - {getSources.isLoading && } - {getSources.isSuccess && ( - ({ label: source.name, value: source.id }))} - name="source" - validation={enrichValidation({ required: true })} - {...{ register, errors, control }} - /> - )} - - - - - {t("Select a Action")} - - {getActions.isLoading && } - {getActions.isSuccess && syncActions && ( - ({ label: action.name, value: action.id }))} - name="action" - {...{ register, errors, control }} - /> - )} - - - - - {t("ExternalId")} - - - - - - {t("Endpoint")} - - - -
-
- -
- ); -}; diff --git a/pwa/src/templates/templateParts/syncForm/CreateSyncTemplate.tsx b/pwa/src/templates/templateParts/syncForm/CreateSyncTemplate.tsx new file mode 100644 index 00000000..cafc6cbe --- /dev/null +++ b/pwa/src/templates/templateParts/syncForm/CreateSyncTemplate.tsx @@ -0,0 +1,24 @@ +import * as React from "react"; +import * as styles from "./SyncFormTemplate.module.css"; + +import { useTranslation } from "react-i18next"; +import { SyncFormTemplate, formId } from "./SyncFormTemplate"; +import { useIsLoadingContext } from "../../../context/isLoading"; +import { FormHeaderTemplate } from "../formHeader/FormHeaderTemplate"; + +interface CreateSyncTemplateProps { + objectId: string; +} + +export const CreateSyncTemplate: React.FC = ({ objectId }) => { + const { t } = useTranslation(); + const { isLoading } = useIsLoadingContext(); + + return ( +
+ + + +
+ ); +}; diff --git a/pwa/src/templates/templateParts/syncForm/EditSyncFormTemplate.tsx b/pwa/src/templates/templateParts/syncForm/EditSyncFormTemplate.tsx deleted file mode 100644 index 6c2091a0..00000000 --- a/pwa/src/templates/templateParts/syncForm/EditSyncFormTemplate.tsx +++ /dev/null @@ -1,171 +0,0 @@ -import * as React from "react"; -import * as styles from "./SyncFormTemplate.module.css"; -import { useForm } from "react-hook-form"; -import FormField, { FormFieldInput, FormFieldLabel } from "@gemeente-denhaag/form-field"; -import { Heading1 } from "@gemeente-denhaag/components-react"; -import { useTranslation } from "react-i18next"; -import { InputText, SelectSingle } from "@conduction/components"; -import { faSave } from "@fortawesome/free-solid-svg-icons"; -import { useQueryClient } from "react-query"; -import { useSource } from "../../../hooks/source"; -import { useAction } from "../../../hooks/action"; -import Skeleton from "react-loading-skeleton"; -import { useSync } from "../../../hooks/synchronization"; -import { Button } from "../../../components/button/Button"; -import { enrichValidation } from "../../../services/enrichReactHookFormValidation"; - -interface EditSyncFormTemplateProps { - objectId: string; - syncId: string; - sync: any; -} - -export const EditSyncFormTemplate: React.FC = ({ objectId, syncId, sync }) => { - const { t } = useTranslation(); - const [loading, setLoading] = React.useState(false); - - const queryClient = useQueryClient(); - - const _useSource = useSource(queryClient); - const getSources = _useSource.getAll(); - const _useAction = useAction(queryClient); - const getActions = _useAction.getAll(); - - const _useSync = useSync(queryClient); - const syncObject = _useSync.createOrEdit(objectId, syncId); - - const getSource = _useSource.getOne(sync?.gateway?.id); - const getAction = _useAction.getOne(sync?.action?.id); - - const syncActions = - getActions && - getActions.data?.filter((action) => action.class === "App\\ActionHandler\\SynchronizationItemHandler"); - - const { - register, - handleSubmit, - watch, - setValue, - control, - formState: { errors }, - } = useForm(); - - const onSubmit = (data: any): void => { - const payload = { - ...data, - }; - - const sourceId = data.source.value; - - syncObject.mutate({ objectId, sourceId, payload, syncId }); - }; - - const handleSetFormValues = (sync: any): void => { - const basicFields: string[] = ["endpoint"]; - basicFields.forEach((field) => setValue(field, sync[field])); - setValue("externalId", sync.sourceId); - }; - - const handleSetSelectActionFormValues = (): void => { - getAction && setValue("action", { label: getAction.data?.name, value: getAction.data?.id }); - }; - const handleSetSelectSourceFormValues = (): void => { - getSource.isSuccess && setValue("source", { label: getSource.data.name, value: getSource.data.id }); - }; - - React.useEffect(() => { - handleSetFormValues(sync); - }, [sync]); - - React.useEffect(() => { - handleSetSelectActionFormValues(); - }, [getAction.isSuccess]); - - React.useEffect(() => { - handleSetSelectSourceFormValues(); - }, [getSource.isSuccess]); - - return ( -
-
-
- {`Edit ${sync?.name}`} - -
-
-
-
-
- - - {t("Select a source")} - - {(getSources.isLoading || getSource.isLoading || getSource.isIdle) && } - {getSources.isSuccess && getSource.isSuccess && ( - ({ label: source.name, value: source.id }))} - name="source" - validation={enrichValidation({ required: true })} - {...{ register, errors, control }} - /> - )} - - - - - {t("Select a Action")} - {sync?.action && ( - <> - {getAction.isLoading && } - {getActions.isSuccess && syncActions && getAction.isSuccess && ( - ({ label: action.name, value: action.id }))} - name="action" - {...{ register, errors, control }} - /> - )} - - )} - {!sync?.action && ( - <> - {(getActions.isLoading || !sync) && } - {getActions.isSuccess && syncActions && ( - ({ label: action.name, value: action.id }))} - name="action" - {...{ register, errors, control }} - /> - )} - - )} - - - - - {t("ExternalId")} - - - - - - {t("Endpoint")} - - - -
-
-
-
- ); -}; diff --git a/pwa/src/templates/templateParts/syncForm/SyncFormTemplate.tsx b/pwa/src/templates/templateParts/syncForm/SyncFormTemplate.tsx new file mode 100644 index 00000000..d892a70c --- /dev/null +++ b/pwa/src/templates/templateParts/syncForm/SyncFormTemplate.tsx @@ -0,0 +1,201 @@ +import * as React from "react"; +import * as styles from "./SyncFormTemplate.module.css"; +import { useForm } from "react-hook-form"; +import FormField, { FormFieldInput, FormFieldLabel } from "@gemeente-denhaag/form-field"; +import { useTranslation } from "react-i18next"; +import { InputText, SelectSingle } from "@conduction/components"; +import { useQueryClient } from "react-query"; +import { useSource } from "../../../hooks/source"; +import { useAction } from "../../../hooks/action"; +import Skeleton from "react-loading-skeleton"; +import { useSync } from "../../../hooks/synchronization"; +import { useObject } from "../../../hooks/object"; +import { useIsLoadingContext } from "../../../context/isLoading"; +import { enrichValidation } from "../../../services/enrichReactHookFormValidation"; + +interface SyncFormTemplateProps { + objectId: string; + synchronization?: any; +} + +export const formId: string = "synchronization-form"; + +export const SyncFormTemplate: React.FC = ({ objectId, synchronization }) => { + const { t } = useTranslation(); + const { setIsLoading, isLoading } = useIsLoadingContext(); + + const queryClient = useQueryClient(); + const _useObject = useObject(); + const getObject = _useObject.getOne(objectId); + + const _useSource = useSource(queryClient); + const getSource = _useSource.getOne(synchronization?.gateway?.id); + const getSources = _useSource.getAll(); + + const _useAction = useAction(queryClient); + const getAction = _useAction.getOne(synchronization?.action?.id); + const getActions = _useAction.getAll(); + + const _useSync = useSync(queryClient); + const syncObject = _useSync.createOrEdit(objectId, synchronization?.id); + + const syncActions = + getActions && + getActions.data?.filter((action) => action.class === "App\\ActionHandler\\SynchronizationItemHandler"); + + const syncId = synchronization?.id ?? null; + + const { + register, + handleSubmit, + setValue, + control, + formState: { errors }, + } = useForm(); + + const onSubmit = (data: any): void => { + const payload = { + ...data, + entity: getObject.data._self.schema.id, + }; + + syncObject.mutate({ payload, objectId, syncId }); + }; + + const handleSetFormValues = (sync: any): void => { + const basicFields: string[] = ["endpoint", "sourceId"]; + basicFields.forEach((field) => setValue(field, sync[field])); + }; + + const handleSetSelectActionFormValues = (): void => { + setValue("action", { label: getAction.data?.name, value: getAction.data?.id }); + }; + const handleSetSelectSourceFormValues = (): void => { + setValue("source", { label: getSource.data.name, value: getSource.data.id }); + }; + + React.useEffect(() => { + synchronization && handleSetFormValues(synchronization); + getAction.isSuccess && handleSetSelectActionFormValues(); + getSource.isSuccess && handleSetSelectSourceFormValues(); + }, [synchronization]); + + React.useEffect(() => { + getActions.isSuccess && getAction.isSuccess && handleSetSelectActionFormValues(); + }, [getAction.isSuccess, getActions.isSuccess]); + + React.useEffect(() => { + getSources.isSuccess && getSource.isSuccess && handleSetSelectSourceFormValues(); + }, [getSource.isSuccess]); + + React.useEffect(() => { + setIsLoading({ syncForm: syncObject.isLoading }); + }, [syncObject.isLoading]); + + return ( +
+
+
+
+ + + {t("Select a Source")} + + {synchronization && ( + <> + {(getSources.isLoading || getSource.isLoading) && } + + {getSources.isSuccess && getSource.isSuccess && ( + ({ label: source.name, value: source.id }))} + name="source" + disabled={isLoading.syncForm} + validation={enrichValidation({ required: true })} + {...{ register, errors, control }} + /> + )} + + )} + + {!synchronization && ( + <> + {getSources.isLoading && } + + {getSources.isSuccess && ( + ({ label: source.name, value: source.id }))} + name="source" + disabled={isLoading.syncForm} + validation={enrichValidation({ required: true })} + {...{ register, errors, control }} + /> + )} + + )} + + + + + {t("Select a Action")} + + {synchronization && ( + <> + {(getActions.isLoading || getAction.isLoading) && } + + {getActions.isSuccess && getAction.isSuccess && syncActions && ( + ({ label: action.name, value: action.id }))} + name="action" + disabled={isLoading.syncForm} + {...{ register, errors, control }} + /> + )} + + )} + + {!synchronization && ( + <> + {getActions.isLoading && } + + {getActions.isSuccess && syncActions && ( + ({ label: action.name, value: action.id }))} + name="action" + disabled={isLoading.syncForm} + {...{ register, errors, control }} + /> + )} + + )} + + + + + + {t("ExternalId")} + + + + + + + {t("Endpoint")} + + + +
+
+
+
+ ); +};