From 417533b29e52eb53ecc4bb0f1c9c1aeb9ab41215 Mon Sep 17 00:00:00 2001 From: Ayobami Akingbade Date: Sun, 31 Mar 2024 01:21:20 +0100 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(mutations):=20imp?= =?UTF-8?q?rove=20mutations=20type=20safety?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/frontend/components/SchemaForm/index.tsx | 2 +- src/frontend/hooks/auth/preferences.store.ts | 20 +++--- .../configuration/configuration.store.ts | 20 +++--- src/frontend/hooks/data/data.store.ts | 48 +++++-------- src/frontend/lib/data/useMutate/types.ts | 9 +-- .../lib/data/useMutate/useApiMutate.ts | 22 ------ .../useApiMutateOptimisticOptions.ts | 38 +++++++--- .../useWaitForResponseMutationOptions.ts | 20 +++--- .../views/Dashboard/dashboard.store.ts | 67 +++++------------ src/frontend/views/account/account.store.ts | 27 +++---- src/frontend/views/data/_BaseEntityForm.tsx | 2 +- .../entity/Actions/form-actions.store.ts | 51 +++++-------- .../integrations/actions/actions.store.ts | 47 ++++-------- .../integrations/storage/storage.store.ts | 27 +++---- src/frontend/views/roles/permissions.store.ts | 29 +++----- src/frontend/views/roles/roles.store.ts | 39 ++++------ .../Variables/configurations.store.ts | 34 ++++----- src/frontend/views/setup/setup.store.ts | 35 ++++----- src/frontend/views/users/users.store.ts | 72 +++++++------------ 19 files changed, 224 insertions(+), 385 deletions(-) delete mode 100644 src/frontend/lib/data/useMutate/useApiMutate.ts diff --git a/src/frontend/components/SchemaForm/index.tsx b/src/frontend/components/SchemaForm/index.tsx index faba1a134..32f33dd1e 100644 --- a/src/frontend/components/SchemaForm/index.tsx +++ b/src/frontend/components/SchemaForm/index.tsx @@ -19,7 +19,7 @@ const identity = (value: unknown) => value; interface IProps { fields: IAppliedSchemaFormConfig; - onSubmit: (data: T) => Promise; + onSubmit: (data: T) => Promise; initialValues?: Partial; buttonText?: (submitting: boolean) => string; action?: string; diff --git a/src/frontend/hooks/auth/preferences.store.ts b/src/frontend/hooks/auth/preferences.store.ts index b2fa95e33..42800af1c 100644 --- a/src/frontend/hooks/auth/preferences.store.ts +++ b/src/frontend/hooks/auth/preferences.store.ts @@ -1,4 +1,3 @@ -import { useMutation } from "@tanstack/react-query"; import { makeActionRequest } from "frontend/lib/data/makeRequest"; import { useStorageApi } from "frontend/lib/data/useApi"; import { useWaitForResponseMutationOptions } from "frontend/lib/data/useMutate/useWaitForResponseMutationOptions"; @@ -42,9 +41,16 @@ export function useUpsertUserPreferenceMutation( key: T, mutationOptions?: IUpsertConfigMutationOptions ) { - const apiMutateOptions = useWaitForResponseMutationOptions< + return useWaitForResponseMutationOptions< + UserPreferencesValueType, UserPreferencesValueType >({ + mutationFn: async (values) => { + await makeActionRequest("PUT", userPrefrencesApiPath(key), { + data: values, + }); + return values; + }, endpoints: [ userPrefrencesApiPath(key), ...(mutationOptions?.otherEndpoints || []), @@ -54,14 +60,4 @@ export function useUpsertUserPreferenceMutation( }, successMessage: MAKE_USER_PREFERENCE_CRUD_CONFIG(key).MUTATION_LANG.SAVED, }); - - return useMutation({ - mutationFn: async (values: UserPreferencesValueType) => { - await makeActionRequest("PUT", userPrefrencesApiPath(key), { - data: values, - }); - return values; - }, - ...apiMutateOptions, - }); } diff --git a/src/frontend/hooks/configuration/configuration.store.ts b/src/frontend/hooks/configuration/configuration.store.ts index fd03dcb44..1fe0b7a36 100644 --- a/src/frontend/hooks/configuration/configuration.store.ts +++ b/src/frontend/hooks/configuration/configuration.store.ts @@ -1,4 +1,3 @@ -import { useMutation } from "@tanstack/react-query"; import { APP_CONFIGURATION_CONFIG, AppConfigurationKeys, @@ -82,9 +81,16 @@ export function useUpsertConfigurationMutation( entity?: string, mutationOptions?: IUpsertConfigMutationOptions ) { - const apiMutateOptions = useWaitForResponseMutationOptions< + return useWaitForResponseMutationOptions< + AppConfigurationValueType, AppConfigurationValueType >({ + mutationFn: async (values) => { + await makeActionRequest("PUT", configurationApiPath(key, entity, "PUT"), { + data: values, + }); + return values; + }, endpoints: [ configurationApiPath(key, entity), ...(mutationOptions?.otherEndpoints || []), @@ -94,14 +100,4 @@ export function useUpsertConfigurationMutation( }, successMessage: MAKE_APP_CONFIGURATION_CRUD_CONFIG(key).MUTATION_LANG.SAVED, }); - - return useMutation({ - mutationFn: async (values: AppConfigurationValueType) => { - await makeActionRequest("PUT", configurationApiPath(key, entity, "PUT"), { - data: values, - }); - return values; - }, - ...apiMutateOptions, - }); } diff --git a/src/frontend/hooks/data/data.store.ts b/src/frontend/hooks/data/data.store.ts index e123c5f04..2831d7b0c 100644 --- a/src/frontend/hooks/data/data.store.ts +++ b/src/frontend/hooks/data/data.store.ts @@ -1,6 +1,5 @@ import qs from "qs"; import { useRouter } from "next/router"; -import { useMutation } from "@tanstack/react-query"; import { FieldQueryFilter, FilterOperators } from "shared/types/data"; import { CRUD_CONFIG_NOT_FOUND } from "frontend/lib/crud-config"; import { makeActionRequest } from "frontend/lib/data/makeRequest"; @@ -142,9 +141,12 @@ export function useEntityDataCreationMutation( ) { const entityCrudConfig = useEntityCrudConfig(entity); const router = useRouter(); - const apiMutateOptions = useWaitForResponseMutationOptions< - Record + return useWaitForResponseMutationOptions< + Record, + { id: string } >({ + mutationFn: async (data) => + await makeActionRequest("POST", `/api/data/${entity}`, { data }), endpoints: DATA_MUTATION_ENDPOINTS_TO_CLEAR(entity), onSuccessActionWithFormData: ({ id }) => { option?.onSuccessActionWithFormData(id); @@ -160,12 +162,6 @@ export function useEntityDataCreationMutation( }, }), }); - - return useMutation({ - mutationFn: async (data: Record) => - await makeActionRequest("POST", `/api/data/${entity}`, { data }), - ...apiMutateOptions, - }); } export function useEntityDataUpdationMutation( @@ -173,24 +169,18 @@ export function useEntityDataUpdationMutation( entityId: string ) { const entityCrudConfig = useEntityCrudConfig(entity); - const apiMutateOptions = useWaitForResponseMutationOptions< - Record - >({ - endpoints: [ - ...SINGLE_DATA_MUTATION_ENDPOINTS_TO_CLEAR({ entity, entityId }), - ...DATA_MUTATION_ENDPOINTS_TO_CLEAR(entity), - ], - successMessage: entityCrudConfig.MUTATION_LANG.EDIT, - }); - const metadata = useEntityMetadataDetails({ entity, entityId }); - return useMutation({ - mutationFn: async (data: Record) => + return useWaitForResponseMutationOptions>({ + mutationFn: async (data) => await makeActionRequest("PATCH", `/api/data/${entity}/${entityId}`, { data: { ...data, ...metadata }, }), - ...apiMutateOptions, + endpoints: [ + ...SINGLE_DATA_MUTATION_ENDPOINTS_TO_CLEAR({ entity, entityId }), + ...DATA_MUTATION_ENDPOINTS_TO_CLEAR(entity), + ], + successMessage: entityCrudConfig.MUTATION_LANG.EDIT, }); } @@ -206,9 +196,11 @@ export function useEntityDataDeletionMutation( ) { const router = useRouter(); const entityCrudConfig = useEntityCrudConfig(entity); - const apiMutateOptions = useWaitForResponseMutationOptions< - Record - >({ + + // eyes on optimstic delete here + return useWaitForResponseMutationOptions({ + mutationFn: async (id) => + await makeActionRequest("DELETE", `/api/data/${entity}/${id}`), endpoints: [ ...SINGLE_DATA_MUTATION_ENDPOINTS_TO_CLEAR({ entity, entityId }), ...DATA_MUTATION_ENDPOINTS_TO_CLEAR(entity), @@ -220,10 +212,4 @@ export function useEntityDataDeletionMutation( }, successMessage: entityCrudConfig.MUTATION_LANG.DELETE, }); - // eyes on optimstic delete here - return useMutation({ - mutationFn: async (id: string) => - await makeActionRequest("DELETE", `/api/data/${entity}/${id}`), - ...apiMutateOptions, - }); } diff --git a/src/frontend/lib/data/useMutate/types.ts b/src/frontend/lib/data/useMutate/types.ts index de3b78278..c07c13c02 100644 --- a/src/frontend/lib/data/useMutate/types.ts +++ b/src/frontend/lib/data/useMutate/types.ts @@ -2,13 +2,14 @@ export type ToastMessageInput = | { message: string; action: { label: string; action: () => void } } | string; -export interface IApiMutateOptions { +export interface IApiMutateOptions { dataQueryPath: string; otherEndpoints?: string[]; - onMutate: (oldData: T | undefined, form: K) => T; + onMutate: (oldData: T | undefined, form: V) => T; + mutationFn: (form: V) => Promise; successMessage?: ToastMessageInput; - smartSuccessMessage?: (formData: V) => ToastMessageInput; - onSuccessActionWithFormData?: (formData: V) => void; + smartSuccessMessage?: (formData: R) => ToastMessageInput; + onSuccessActionWithFormData?: (formData: R) => void; } export const FOR_CODE_COV = 1; diff --git a/src/frontend/lib/data/useMutate/useApiMutate.ts b/src/frontend/lib/data/useMutate/useApiMutate.ts deleted file mode 100644 index 7e570a627..000000000 --- a/src/frontend/lib/data/useMutate/useApiMutate.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { useQueryClient } from "@tanstack/react-query"; -import { getQueryCachekey } from "../constants/getQueryCacheKey"; - -export function useApiMutate(endpoint: string) { - const queryClient = useQueryClient(); - const queryKey = getQueryCachekey(endpoint); - - return { - set: async (mutateOldData: (oldData: T | undefined) => T) => { - await queryClient.cancelQueries({ queryKey }); - const previousData = queryClient.getQueryData(queryKey); - queryClient.setQueryData(queryKey, mutateOldData); - return previousData; - }, - invalidate: () => { - queryClient.invalidateQueries({ queryKey }); - }, - reset: (previousData: T | undefined) => { - queryClient.setQueryData(queryKey, previousData); - }, - }; -} diff --git a/src/frontend/lib/data/useMutate/useApiMutateOptimisticOptions.ts b/src/frontend/lib/data/useMutate/useApiMutateOptimisticOptions.ts index 8626a785a..d77b6a65d 100644 --- a/src/frontend/lib/data/useMutate/useApiMutateOptimisticOptions.ts +++ b/src/frontend/lib/data/useMutate/useApiMutateOptimisticOptions.ts @@ -1,20 +1,40 @@ import { noop } from "shared/lib/noop"; -import { useQueryClient } from "@tanstack/react-query"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; import { ToastService } from "frontend/lib/toast"; import { IApiMutateOptions } from "./types"; -import { useApiMutate } from "./useApiMutate"; import { getQueryCachekey } from "../constants/getQueryCacheKey"; -export function useApiMutateOptimisticOptions( - options: IApiMutateOptions +function useApiMutate(endpoint: string) { + const queryClient = useQueryClient(); + const queryKey = getQueryCachekey(endpoint); + + return { + set: async (mutateOldData: (oldData: T | undefined) => T) => { + await queryClient.cancelQueries({ queryKey }); + const previousData = queryClient.getQueryData(queryKey); + queryClient.setQueryData(queryKey, mutateOldData); + return previousData; + }, + invalidate: () => { + queryClient.invalidateQueries({ queryKey }); + }, + reset: (previousData: T | undefined) => { + queryClient.setQueryData(queryKey, previousData); + }, + }; +} + +export function useApiMutateOptimisticOptions( + options: IApiMutateOptions ) { const apiMutate = useApiMutate(options.dataQueryPath); const queryClient = useQueryClient(); - return { - onMutate: async (formData: K) => + return useMutation({ + mutationFn: options.mutationFn, + onMutate: async (formData: V) => apiMutate.set((oldData) => options.onMutate(oldData, formData)), - onSuccess: async (requestResponse: V) => { + onSuccess: async (requestResponse: R) => { if (options.smartSuccessMessage) { ToastService.success(options.smartSuccessMessage(requestResponse)); } else if (options.successMessage) { @@ -33,7 +53,7 @@ export function useApiMutateOptimisticOptions( }, onError: ( error: { message: string }, - formData: K, + formData: V, oldData: T | undefined ) => { noop(formData, error); @@ -46,5 +66,5 @@ export function useApiMutateOptimisticOptions( onSettled: () => { apiMutate.invalidate(); }, - }; + }); } diff --git a/src/frontend/lib/data/useMutate/useWaitForResponseMutationOptions.ts b/src/frontend/lib/data/useMutate/useWaitForResponseMutationOptions.ts index 57e490cfb..7a898110a 100644 --- a/src/frontend/lib/data/useMutate/useWaitForResponseMutationOptions.ts +++ b/src/frontend/lib/data/useMutate/useWaitForResponseMutationOptions.ts @@ -1,26 +1,28 @@ -import { useQueryClient } from "@tanstack/react-query"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; import { ToastService } from "frontend/lib/toast"; import { getQueryCachekey } from "../constants/getQueryCacheKey"; import { ToastMessageInput } from "./types"; -export interface IWaitForResponseMutationOptions { +export interface IWaitForResponseMutationOptions { endpoints: string[]; redirect?: string; - onSuccessActionWithFormData?: (formData: T) => void; + mutationFn: (formData: V) => Promise; + onSuccessActionWithFormData?: (response: R) => void; successMessage?: ToastMessageInput; - smartSuccessMessage?: (formData: T) => ToastMessageInput; + smartSuccessMessage?: (formData: R) => ToastMessageInput; } const PASS_DATA_FROM_HANDLER_ERROR_MESSAGE = "Please return in the mutation what data you want to pass to the success handlers"; -export function useWaitForResponseMutationOptions( - options: IWaitForResponseMutationOptions +export function useWaitForResponseMutationOptions( + options: IWaitForResponseMutationOptions ) { const queryClient = useQueryClient(); - return { - onSuccess: async (formData: T | undefined) => { + return useMutation({ + mutationFn: options.mutationFn, + onSuccess: async (formData: R | undefined) => { options.endpoints.forEach((queryKey) => { queryClient.invalidateQueries({ queryKey: getQueryCachekey(queryKey) }); }); @@ -46,5 +48,5 @@ export function useWaitForResponseMutationOptions( error.message || "Something went wrong. Please try again" ); }, - }; + }); } diff --git a/src/frontend/views/Dashboard/dashboard.store.ts b/src/frontend/views/Dashboard/dashboard.store.ts index f7330afc4..05acaa12c 100644 --- a/src/frontend/views/Dashboard/dashboard.store.ts +++ b/src/frontend/views/Dashboard/dashboard.store.ts @@ -1,4 +1,3 @@ -import { useMutation } from "@tanstack/react-query"; import { IWidgetConfig } from "shared/types/dashboard"; import { CRUD_CONFIG_NOT_FOUND } from "frontend/lib/crud-config"; import { makeActionRequest } from "frontend/lib/data/makeRequest"; @@ -44,61 +43,38 @@ export const useDasboardWidgetScriptData = ( }; export function useCreateDashboardWidgetMutation(dashboardId: string) { - const apiMutateOptions = useApiMutateOptimisticOptions< - IWidgetConfig[], - IWidgetConfig - >({ + return useApiMutateOptimisticOptions({ + mutationFn: async (widget) => { + await makeActionRequest("POST", DASHBOARD_ENDPOINT(dashboardId), widget); + }, dataQueryPath: DASHBOARD_ENDPOINT(dashboardId), onMutate: MutationHelpers.append, successMessage: DASHBOARD_WIDGETS_CRUD_CONFIG.MUTATION_LANG.CREATE, }); - - return useMutation({ - mutationFn: async (widget: IWidgetConfig) => { - await makeActionRequest("POST", DASHBOARD_ENDPOINT(dashboardId), widget); - }, - ...apiMutateOptions, - }); } export function useUpdateDashboardWidgetMutation( dashboardId: string, widgetId: string ) { - const apiMutateOptions = useApiMutateOptimisticOptions< - IWidgetConfig[], - IWidgetConfig - >({ - dataQueryPath: DASHBOARD_ENDPOINT(dashboardId), - otherEndpoints: [DASHBOARD_WIDGET_SCRIPT_ENDPOINT(widgetId)], - onMutate: MutationHelpers.update, - successMessage: DASHBOARD_WIDGETS_CRUD_CONFIG.MUTATION_LANG.EDIT, - }); - - return useMutation({ - mutationFn: async (widget: IWidgetConfig) => { + return useApiMutateOptimisticOptions({ + mutationFn: async (widget) => { await makeActionRequest( "PATCH", `${DASHBOARD_ENDPOINT(dashboardId)}/${widget.id}`, widget ); }, - ...apiMutateOptions, + dataQueryPath: DASHBOARD_ENDPOINT(dashboardId), + otherEndpoints: [DASHBOARD_WIDGET_SCRIPT_ENDPOINT(widgetId)], + onMutate: MutationHelpers.update, + successMessage: DASHBOARD_WIDGETS_CRUD_CONFIG.MUTATION_LANG.EDIT, }); } export function useDeleteDashboardWidgetMutation(dashboardId: string) { - const apiMutateOptions = useApiMutateOptimisticOptions< - IWidgetConfig[], - string - >({ - dataQueryPath: DASHBOARD_ENDPOINT(dashboardId), - onMutate: MutationHelpers.delete, - successMessage: DASHBOARD_WIDGETS_CRUD_CONFIG.MUTATION_LANG.DELETE, - }); - - return useMutation({ - mutationFn: async (widgetId: string) => { + return useApiMutateOptimisticOptions({ + mutationFn: async (widgetId) => { await makeActionRequest( "DELETE", `${DASHBOARD_ENDPOINT(dashboardId)}/${widgetId}`, @@ -107,27 +83,22 @@ export function useDeleteDashboardWidgetMutation(dashboardId: string) { } ); }, - ...apiMutateOptions, + dataQueryPath: DASHBOARD_ENDPOINT(dashboardId), + onMutate: MutationHelpers.delete, + successMessage: DASHBOARD_WIDGETS_CRUD_CONFIG.MUTATION_LANG.DELETE, }); } export function useArrangeDashboardWidgetMutation(dashboardId: string) { - const apiMutateOptions = useApiMutateOptimisticOptions< - IWidgetConfig[], - string[] - >({ - dataQueryPath: DASHBOARD_ENDPOINT(dashboardId), - onMutate: MutationHelpers.sortOrder, - }); - - return useMutation({ - mutationFn: async (widgetList: string[]) => { + return useApiMutateOptimisticOptions({ + mutationFn: async (widgetList) => { await makeActionRequest( "PATCH", DASHBOARD_ENDPOINT(dashboardId), widgetList ); }, - ...apiMutateOptions, + dataQueryPath: DASHBOARD_ENDPOINT(dashboardId), + onMutate: MutationHelpers.sortOrder, }); } diff --git a/src/frontend/views/account/account.store.ts b/src/frontend/views/account/account.store.ts index f260f7e87..ca771c346 100644 --- a/src/frontend/views/account/account.store.ts +++ b/src/frontend/views/account/account.store.ts @@ -1,5 +1,4 @@ import { AUTHENTICATED_ACCOUNT_URL } from "frontend/hooks/auth/user.store"; -import { useMutation } from "@tanstack/react-query"; import { IChangePasswordForm } from "shared/form-schemas/profile/password"; import { IUpdateProfileForm } from "shared/form-schemas/profile/update"; import { ACCOUNT_PROFILE_CRUD_CONFIG } from "frontend/hooks/auth/constants"; @@ -8,34 +7,26 @@ import { makeActionRequest } from "frontend/lib/data/makeRequest"; import { PASSWORD_CRUD_CONFIG } from "./constants"; export function useUpdateProfileMutation() { - const apiMutateOptions = useWaitForResponseMutationOptions({ + return useWaitForResponseMutationOptions({ + mutationFn: async (data) => + await makeActionRequest("PATCH", AUTHENTICATED_ACCOUNT_URL, data), endpoints: [AUTHENTICATED_ACCOUNT_URL], successMessage: ACCOUNT_PROFILE_CRUD_CONFIG.MUTATION_LANG.SAVED, }); - - return useMutation({ - mutationFn: async (data: IUpdateProfileForm) => - await makeActionRequest("PATCH", AUTHENTICATED_ACCOUNT_URL, data), - ...apiMutateOptions, - }); } export function useChangePasswordMutation() { - const apiMutateOptions = useWaitForResponseMutationOptions({ - endpoints: [], - successMessage: process.env.NEXT_PUBLIC_IS_DEMO - ? "Password will not be changed on demo account" - : PASSWORD_CRUD_CONFIG.MUTATION_LANG.EDIT, - }); - - return useMutation({ - mutationFn: async (data: IChangePasswordForm) => { + return useWaitForResponseMutationOptions({ + mutationFn: async (data) => { return await makeActionRequest( "PATCH", `/api/account/change-password`, data ); }, - ...apiMutateOptions, + endpoints: [], + successMessage: process.env.NEXT_PUBLIC_IS_DEMO + ? "Password will not be changed on demo account" + : PASSWORD_CRUD_CONFIG.MUTATION_LANG.EDIT, }); } diff --git a/src/frontend/views/data/_BaseEntityForm.tsx b/src/frontend/views/data/_BaseEntityForm.tsx index 08e5af887..3f78ca681 100644 --- a/src/frontend/views/data/_BaseEntityForm.tsx +++ b/src/frontend/views/data/_BaseEntityForm.tsx @@ -26,7 +26,7 @@ type IProps = { initialValuesData?: DataStateKeys>; crudAction: "create" | "update"; allOptional?: boolean; - onSubmit: (data: Record) => Promise; + onSubmit: (data: Record) => Promise; resetForm?: true; buttonText: (submitting: boolean) => string; systemIcon: SystemIconsKeys; diff --git a/src/frontend/views/entity/Actions/form-actions.store.ts b/src/frontend/views/entity/Actions/form-actions.store.ts index e64c3ced1..490719286 100644 --- a/src/frontend/views/entity/Actions/form-actions.store.ts +++ b/src/frontend/views/entity/Actions/form-actions.store.ts @@ -1,5 +1,4 @@ import { CRUD_CONFIG_NOT_FOUND } from "frontend/lib/crud-config"; -import { useMutation } from "@tanstack/react-query"; import { IFormAction, IIntegrationImplementationList, @@ -28,62 +27,44 @@ export const useIntegrationImplementationsList = (integration: string) => ); export function useDeleteFormActionMutation(entity: string) { - const apiMutateOptions = useApiMutateOptimisticOptions( - { - dataQueryPath: LIST_ENTITY_FORM_ACTIONS(entity), - successMessage: FORM_ACTION_CRUD_CONFIG.MUTATION_LANG.DELETE, - onMutate: MutationHelpers.deleteByKey("id") as unknown as ( - oldData: IFormAction[], - form: string - ) => IFormAction[], - } - ); - - return useMutation({ - mutationFn: async (formActionId: string) => + return useApiMutateOptimisticOptions({ + mutationFn: async (formActionId) => await makeActionRequest( "DELETE", FORM_ACTION_CRUD_CONFIG.ENDPOINTS.DELETE(formActionId) ), - ...apiMutateOptions, + dataQueryPath: LIST_ENTITY_FORM_ACTIONS(entity), + successMessage: FORM_ACTION_CRUD_CONFIG.MUTATION_LANG.DELETE, + onMutate: MutationHelpers.deleteByKey("id") as unknown as ( + oldData: IFormAction[], + form: string + ) => IFormAction[], }); } export function useCreateFormActionMutation(entity: string) { - const apiMutateOptions = useWaitForResponseMutationOptions< - Record - >({ - endpoints: [LIST_ENTITY_FORM_ACTIONS(entity)], - successMessage: FORM_ACTION_CRUD_CONFIG.MUTATION_LANG.CREATE, - }); - - return useMutation({ - mutationFn: async (configuration: IFormAction) => { + return useWaitForResponseMutationOptions({ + mutationFn: async (configuration) => { return await makeActionRequest( "POST", FORM_ACTION_CRUD_CONFIG.ENDPOINTS.CREATE, configuration ); }, - ...apiMutateOptions, + endpoints: [LIST_ENTITY_FORM_ACTIONS(entity)], + successMessage: FORM_ACTION_CRUD_CONFIG.MUTATION_LANG.CREATE, }); } export function useUpdateFormActionMutation(entity: string) { - const apiMutateOptions = useWaitForResponseMutationOptions< - Record - >({ - endpoints: [LIST_ENTITY_FORM_ACTIONS(entity)], - successMessage: FORM_ACTION_CRUD_CONFIG.MUTATION_LANG.EDIT, - }); - - return useMutation({ - mutationFn: async (formAction: IFormAction) => + return useWaitForResponseMutationOptions({ + mutationFn: async (formAction) => await makeActionRequest( "PATCH", FORM_ACTION_CRUD_CONFIG.ENDPOINTS.UPDATE(formAction.id), formAction ), - ...apiMutateOptions, + endpoints: [LIST_ENTITY_FORM_ACTIONS(entity)], + successMessage: FORM_ACTION_CRUD_CONFIG.MUTATION_LANG.EDIT, }); } diff --git a/src/frontend/views/integrations/actions/actions.store.ts b/src/frontend/views/integrations/actions/actions.store.ts index e1d7d7fed..37455f60d 100644 --- a/src/frontend/views/integrations/actions/actions.store.ts +++ b/src/frontend/views/integrations/actions/actions.store.ts @@ -1,4 +1,3 @@ -import { useMutation } from "@tanstack/react-query"; import { IIntegrationsList, ActionIntegrations } from "shared/types/actions"; import { CRUD_CONFIG_NOT_FOUND } from "frontend/lib/crud-config"; import { reduceStringToNumber } from "shared/lib/strings"; @@ -48,56 +47,37 @@ export const useActivationConfiguration = (activationId: string) => { }; export function useDeactivateIntegrationMutation() { - const apiMutateOptions = useWaitForResponseMutationOptions< - Record - >({ - endpoints: [ACTIVE_ACTIONS_INTEGRATIONS_ENDPOINT], - successMessage: - ACTION_INTEGRATIONS_CRUD_CONFIG.MUTATION_LANG.CUSTOM("Deactivated"), - }); - - return useMutation({ - mutationFn: async (activationId: string) => + return useWaitForResponseMutationOptions({ + mutationFn: async (activationId) => await makeActionRequest( "DELETE", `/api/integrations/actions/${activationId}` ), - ...apiMutateOptions, - }); -} - -export function useActivateIntegrationMutation(integration: string) { - const apiMutateOptions = useWaitForResponseMutationOptions< - Record - >({ endpoints: [ACTIVE_ACTIONS_INTEGRATIONS_ENDPOINT], successMessage: - ACTION_INTEGRATIONS_CRUD_CONFIG.MUTATION_LANG.CUSTOM("Activated"), + ACTION_INTEGRATIONS_CRUD_CONFIG.MUTATION_LANG.CUSTOM("Deactivated"), }); +} - return useMutation({ - mutationFn: async (configuration: Record) => +export function useActivateIntegrationMutation(integration: string) { + return useWaitForResponseMutationOptions>({ + mutationFn: async (configuration) => await makeActionRequest( "POST", `/api/integrations/actions/${integration}`, configuration ), - ...apiMutateOptions, + endpoints: [ACTIVE_ACTIONS_INTEGRATIONS_ENDPOINT], + successMessage: + ACTION_INTEGRATIONS_CRUD_CONFIG.MUTATION_LANG.CUSTOM("Activated"), }); } export function useUpdateActivatedIntegrationMutation(activationId: string) { - const apiMutateOptions = useWaitForResponseMutationOptions< - Record - >({ - endpoints: [ACTIVE_ACTIONS_INTEGRATIONS_ENDPOINT], - successMessage: ACTION_INTEGRATIONS_CRUD_CONFIG.MUTATION_LANG.EDIT, - }); - const rootPassword = usePasswordStore((state) => state.password); - return useMutation({ - mutationFn: async (configuration: Record) => + return useWaitForResponseMutationOptions>({ + mutationFn: async (configuration) => await makeActionRequest( "PATCH", `/api/integrations/actions/${activationId}`, @@ -106,6 +86,7 @@ export function useUpdateActivatedIntegrationMutation(activationId: string) { _password: rootPassword, } ), - ...apiMutateOptions, + endpoints: [ACTIVE_ACTIONS_INTEGRATIONS_ENDPOINT], + successMessage: ACTION_INTEGRATIONS_CRUD_CONFIG.MUTATION_LANG.EDIT, }); } diff --git a/src/frontend/views/integrations/storage/storage.store.ts b/src/frontend/views/integrations/storage/storage.store.ts index 4e4332142..87b43afee 100644 --- a/src/frontend/views/integrations/storage/storage.store.ts +++ b/src/frontend/views/integrations/storage/storage.store.ts @@ -1,4 +1,3 @@ -import { useMutation } from "@tanstack/react-query"; import { CRUD_CONFIG_NOT_FOUND } from "frontend/lib/crud-config"; import { reduceStringToNumber } from "shared/lib/strings"; import { useApi } from "frontend/lib/data/useApi"; @@ -43,9 +42,16 @@ export const useStorageCredentialsConfiguration = () => { }; export function useActivateStorageMutation() { - const apiMutateOptions = useWaitForResponseMutationOptions< - Record - >({ + return useWaitForResponseMutationOptions<{ + storageKey: string; + configuration: Record; + }>({ + mutationFn: async (configuration) => + await makeActionRequest( + "POST", + ACTIVE_STORAGE_INTEGRATIONS_ENDPOINT, + configuration + ), endpoints: [ ACTIVE_STORAGE_INTEGRATIONS_ENDPOINT, STORAGE_CREDENTIALS_CONFIG, @@ -53,17 +59,4 @@ export function useActivateStorageMutation() { successMessage: STORAGE_INTEGRATIONS_CRUD_CONFIG.MUTATION_LANG.CUSTOM("Activated"), }); - - return useMutation({ - mutationFn: async (configuration: { - storageKey: string; - configuration: Record; - }) => - await makeActionRequest( - "POST", - ACTIVE_STORAGE_INTEGRATIONS_ENDPOINT, - configuration - ), - ...apiMutateOptions, - }); } diff --git a/src/frontend/views/roles/permissions.store.ts b/src/frontend/views/roles/permissions.store.ts index 685fb1f71..6f886f686 100644 --- a/src/frontend/views/roles/permissions.store.ts +++ b/src/frontend/views/roles/permissions.store.ts @@ -1,4 +1,3 @@ -import { useMutation } from "@tanstack/react-query"; import { MAKE_CRUD_CONFIG } from "frontend/lib/crud-config"; import { useStorageApi } from "frontend/lib/data/useApi"; import { useApiMutateOptimisticOptions } from "frontend/lib/data/useMutate/useApiMutateOptimisticOptions"; @@ -28,14 +27,8 @@ export function useRolePermissions() { export function useRolePermissionDeletionMutation() { const roleId = useRoleIdFromRouteParam(); - const apiMutateOptions = useApiMutateOptimisticOptions({ - dataQueryPath: ADMIN_ROLE_PERMISSION_ENDPOINT(roleId), - onMutate: MutationHelpers.removeMany, - successMessage: ADMIN_PERMISSIONS_CRUD_CONFIG.MUTATION_LANG.DELETE, - }); - - return useMutation({ - mutationFn: async (permissions: string[]) => { + return useApiMutateOptimisticOptions({ + mutationFn: async (permissions) => { await makeActionRequest( "DELETE", ADMIN_ROLE_PERMISSION_ENDPOINT(roleId), @@ -44,24 +37,22 @@ export function useRolePermissionDeletionMutation() { } ); }, - ...apiMutateOptions, + dataQueryPath: ADMIN_ROLE_PERMISSION_ENDPOINT(roleId), + onMutate: MutationHelpers.removeMany, + successMessage: ADMIN_PERMISSIONS_CRUD_CONFIG.MUTATION_LANG.DELETE, }); } export function useCreateRolePermissionMutation() { const roleId = useRoleIdFromRouteParam(); - const apiMutateOptions = useApiMutateOptimisticOptions({ - dataQueryPath: ADMIN_ROLE_PERMISSION_ENDPOINT(roleId), - onMutate: MutationHelpers.mergeArray, - successMessage: ADMIN_PERMISSIONS_CRUD_CONFIG.MUTATION_LANG.CREATE, - }); - - return useMutation({ - mutationFn: async (permissions: string[]) => + return useApiMutateOptimisticOptions({ + mutationFn: async (permissions) => await makeActionRequest("POST", ADMIN_ROLE_PERMISSION_ENDPOINT(roleId), { permissions, }), - ...apiMutateOptions, + dataQueryPath: ADMIN_ROLE_PERMISSION_ENDPOINT(roleId), + onMutate: MutationHelpers.mergeArray, + successMessage: ADMIN_PERMISSIONS_CRUD_CONFIG.MUTATION_LANG.CREATE, }); } diff --git a/src/frontend/views/roles/roles.store.ts b/src/frontend/views/roles/roles.store.ts index 53ec888e7..3b82f8943 100644 --- a/src/frontend/views/roles/roles.store.ts +++ b/src/frontend/views/roles/roles.store.ts @@ -1,6 +1,5 @@ import { NAVIGATION_LINKS } from "frontend/lib/routing/links"; import { useRouter } from "next/router"; -import { useMutation } from "@tanstack/react-query"; import { IBaseRoleForm } from "shared/form-schemas/roles/base"; import { makeRoleId } from "shared/constants/user"; import { IRolesList } from "shared/types/roles"; @@ -18,7 +17,15 @@ export const ADMIN_ROLES_CRUD_CONFIG = MAKE_CRUD_CONFIG({ export function useCreateRoleMutation() { const router = useRouter(); - const apiMutateOptions = useWaitForResponseMutationOptions({ + return useWaitForResponseMutationOptions({ + mutationFn: async (data) => { + await makeActionRequest( + "POST", + ADMIN_ROLES_CRUD_CONFIG.ENDPOINTS.CREATE, + data + ); + return data; + }, endpoints: [ADMIN_ROLES_CRUD_CONFIG.ENDPOINTS.LIST], smartSuccessMessage: ({ name }) => ({ message: ADMIN_ROLES_CRUD_CONFIG.MUTATION_LANG.CREATE, @@ -29,23 +36,16 @@ export function useCreateRoleMutation() { }, }), }); - - return useMutation({ - mutationFn: async (data: IBaseRoleForm) => { - await makeActionRequest( - "POST", - ADMIN_ROLES_CRUD_CONFIG.ENDPOINTS.CREATE, - data - ); - return data; - }, - ...apiMutateOptions, - }); } export function useRoleDeletionMutation() { const router = useRouter(); - const apiMutateOptions = useApiMutateOptimisticOptions({ + return useApiMutateOptimisticOptions({ + mutationFn: async (roleId) => + await makeActionRequest( + "DELETE", + ADMIN_ROLES_CRUD_CONFIG.ENDPOINTS.DELETE(roleId) + ), dataQueryPath: ADMIN_ROLES_CRUD_CONFIG.ENDPOINTS.LIST, onSuccessActionWithFormData: () => { router.replace(NAVIGATION_LINKS.ROLES.LIST); @@ -53,13 +53,4 @@ export function useRoleDeletionMutation() { onMutate: MutationHelpers.deleteByKey("value"), successMessage: ADMIN_ROLES_CRUD_CONFIG.MUTATION_LANG.DELETE, }); - - return useMutation({ - mutationFn: async (roleId: string) => - await makeActionRequest( - "DELETE", - ADMIN_ROLES_CRUD_CONFIG.ENDPOINTS.DELETE(roleId) - ), - ...apiMutateOptions, - }); } diff --git a/src/frontend/views/settings/Variables/configurations.store.ts b/src/frontend/views/settings/Variables/configurations.store.ts index 282de395e..b8876fa01 100644 --- a/src/frontend/views/settings/Variables/configurations.store.ts +++ b/src/frontend/views/settings/Variables/configurations.store.ts @@ -1,6 +1,6 @@ import { usePasswordStore } from "frontend/views/integrations/password.store"; import { useEffect } from "react"; -import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { useQueryClient } from "@tanstack/react-query"; import { IntegrationsConfigurationGroup } from "shared/types/integrations"; import { CRUD_CONFIG_NOT_FOUND } from "frontend/lib/crud-config"; import { makeActionRequest } from "frontend/lib/data/makeRequest"; @@ -24,24 +24,18 @@ export function useIntegrationConfigurationUpsertationMutation( ) { const rootPassword = usePasswordStore((state) => state.password); - const apiMutateOptions = useWaitForResponseMutationOptions< - Record - >({ + return useWaitForResponseMutationOptions<{ key: string; value: string }>({ + mutationFn: async (data) => + await makeActionRequest("PUT", `/api/integrations/${group}/${data.key}`, { + value: data.value, + _password: rootPassword, + }), endpoints: rootPassword ? [REVEAL_CREDENTIALS_ENDPOINT, INTEGRATIONS_GROUP_ENDPOINT(group)] : [INTEGRATIONS_GROUP_ENDPOINT(group)], successMessage: INTEGRATIONS_GROUP_CRUD_CONFIG[group].crudConfig.MUTATION_LANG.SAVED, }); - - return useMutation({ - mutationFn: async (data: { key: string; value: string }) => - await makeActionRequest("PUT", `/api/integrations/${group}/${data.key}`, { - value: data.value, - _password: rootPassword, - }), - ...apiMutateOptions, - }); } export function useIntegrationConfigurationDeletionMutation( @@ -49,21 +43,17 @@ export function useIntegrationConfigurationDeletionMutation( ) { const rootPassword = usePasswordStore((state) => state.password); - const apiMutateOptions = useApiMutateOptimisticOptions({ + return useApiMutateOptimisticOptions({ + mutationFn: async (key) => + await makeActionRequest("DELETE", `/api/integrations/${group}/${key}`, { + _password: rootPassword, + }), dataQueryPath: INTEGRATIONS_GROUP_ENDPOINT(group), otherEndpoints: rootPassword ? [REVEAL_CREDENTIALS_ENDPOINT] : [], successMessage: INTEGRATIONS_GROUP_CRUD_CONFIG[group].crudConfig.MUTATION_LANG.DELETE, onMutate: MutationHelpers.deleteByKey("key"), }); - - return useMutation({ - mutationFn: async (key: string) => - await makeActionRequest("DELETE", `/api/integrations/${group}/${key}`, { - _password: rootPassword, - }), - ...apiMutateOptions, - }); } export const useRevealedCredentialsList = ( diff --git a/src/frontend/views/setup/setup.store.ts b/src/frontend/views/setup/setup.store.ts index 330af44c9..f82beed90 100644 --- a/src/frontend/views/setup/setup.store.ts +++ b/src/frontend/views/setup/setup.store.ts @@ -2,41 +2,32 @@ import { useAuthenticateUser } from "frontend/hooks/auth/useAuthenticateUser"; import { SETUP_CHECK_URL } from "frontend/hooks/setup/setup.store"; import { makeActionRequest } from "frontend/lib/data/makeRequest"; import { useWaitForResponseMutationOptions } from "frontend/lib/data/useMutate/useWaitForResponseMutationOptions"; -import { useMutation } from "@tanstack/react-query"; import { ISetupUserForm } from "shared/form-schemas/setup/user"; import { ISuccessfullAuthenticationResponse } from "shared/types/auth/portal"; import { IDataSourceCredentials } from "shared/types/data-sources"; export function useSetupCredentialsMutation() { - const apiMutateOptions = useWaitForResponseMutationOptions< - Record - >({ + return useWaitForResponseMutationOptions({ + mutationFn: async (data) => + await makeActionRequest("POST", `/api/setup/credentials`, data), endpoints: [SETUP_CHECK_URL], successMessage: "Credentials Was Successfully Setup", }); - - return useMutation({ - mutationFn: async (data: IDataSourceCredentials) => - await makeActionRequest("POST", `/api/setup/credentials`, data), - ...apiMutateOptions, - }); } export function useSetupUserMutation() { const authenticateUser = useAuthenticateUser(); - const apiMutateOptions = - useWaitForResponseMutationOptions({ - endpoints: [SETUP_CHECK_URL], - successMessage: "Account Was Successfully Setup", - onSuccessActionWithFormData: (response) => { - authenticateUser(response.token, true); - }, - }); - - return useMutation({ - mutationFn: async (data: ISetupUserForm) => + return useWaitForResponseMutationOptions< + ISetupUserForm, + ISuccessfullAuthenticationResponse + >({ + mutationFn: async (data) => await makeActionRequest("POST", `/api/setup/user`, data), - ...apiMutateOptions, + endpoints: [SETUP_CHECK_URL], + successMessage: "Account Was Successfully Setup", + onSuccessActionWithFormData: (response) => { + authenticateUser(response.token, true); + }, }); } diff --git a/src/frontend/views/users/users.store.ts b/src/frontend/views/users/users.store.ts index df078b35f..db7e3a8c7 100644 --- a/src/frontend/views/users/users.store.ts +++ b/src/frontend/views/users/users.store.ts @@ -1,6 +1,5 @@ import { NAVIGATION_LINKS } from "frontend/lib/routing/links"; import { useRouter } from "next/router"; -import { useMutation } from "@tanstack/react-query"; import { ICreateUserForm } from "shared/form-schemas/users"; import { IResetPasswordForm } from "shared/form-schemas/users/reset-password"; import { IAccountProfile } from "shared/types/user"; @@ -40,10 +39,12 @@ export function useUserDetails(username: string) { export function useUserDeletionMutation() { const router = useRouter(); - const apiMutateOptions = useApiMutateOptimisticOptions< - IAccountProfile[], - string - >({ + return useApiMutateOptimisticOptions({ + mutationFn: async (username) => + await makeActionRequest( + "DELETE", + ADMIN_USERS_CRUD_CONFIG.ENDPOINTS.DELETE(username) + ), dataQueryPath: ADMIN_USERS_CRUD_CONFIG.ENDPOINTS.LIST, onSuccessActionWithFormData: () => { router.replace(NAVIGATION_LINKS.USERS.LIST); @@ -51,70 +52,42 @@ export function useUserDeletionMutation() { onMutate: MutationHelpers.deleteByKey("username"), successMessage: ADMIN_USERS_CRUD_CONFIG.MUTATION_LANG.DELETE, }); - - return useMutation({ - mutationFn: async (username: string) => - await makeActionRequest( - "DELETE", - ADMIN_USERS_CRUD_CONFIG.ENDPOINTS.DELETE(username) - ), - ...apiMutateOptions, - }); } export function useUpdateUserMutation() { const username = useUsernameFromRouteParam(); - const apiMutateOptions = useWaitForResponseMutationOptions({ - endpoints: [ - ADMIN_USERS_CRUD_CONFIG.ENDPOINTS.LIST, - ADMIN_USERS_CRUD_CONFIG.ENDPOINTS.DETAILS(username), - ], - successMessage: ADMIN_USERS_CRUD_CONFIG.MUTATION_LANG.EDIT, - }); - - return useMutation({ - mutationFn: async (data: Partial) => + return useWaitForResponseMutationOptions>({ + mutationFn: async (data) => await makeActionRequest( "PATCH", ADMIN_USERS_CRUD_CONFIG.ENDPOINTS.UPDATE(username), data ), - ...apiMutateOptions, + endpoints: [ + ADMIN_USERS_CRUD_CONFIG.ENDPOINTS.LIST, + ADMIN_USERS_CRUD_CONFIG.ENDPOINTS.DETAILS(username), + ], + successMessage: ADMIN_USERS_CRUD_CONFIG.MUTATION_LANG.EDIT, }); } export function useResetUserPasswordMutation() { const username = useUsernameFromRouteParam(); - const apiMutateOptions = useWaitForResponseMutationOptions({ - endpoints: [], - successMessage: "Password Reset Successfully", - }); - - return useMutation({ - mutationFn: async (data: IResetPasswordForm) => + return useWaitForResponseMutationOptions({ + mutationFn: async (data) => await makeActionRequest( "PATCH", ADMIN_USERS_CRUD_CONFIG.ENDPOINTS.CUSTOM(username, "reset-password"), data ), - ...apiMutateOptions, + endpoints: [], + successMessage: "Password Reset Successfully", }); } export function useCreateUserMutation() { const router = useRouter(); - const apiMutateOptions = useWaitForResponseMutationOptions({ - endpoints: [ADMIN_USERS_CRUD_CONFIG.ENDPOINTS.LIST], - smartSuccessMessage: ({ username }) => ({ - message: ADMIN_USERS_CRUD_CONFIG.MUTATION_LANG.CREATE, - action: { - label: ADMIN_USERS_CRUD_CONFIG.MUTATION_LANG.VIEW_DETAILS, - action: () => router.push(NAVIGATION_LINKS.USERS.DETAILS(username)), - }, - }), - }); - - return useMutation({ + return useWaitForResponseMutationOptions({ mutationFn: async (data: ICreateUserForm) => { await makeActionRequest( "POST", @@ -123,6 +96,13 @@ export function useCreateUserMutation() { ); return data; }, - ...apiMutateOptions, + endpoints: [ADMIN_USERS_CRUD_CONFIG.ENDPOINTS.LIST], + smartSuccessMessage: ({ username }) => ({ + message: ADMIN_USERS_CRUD_CONFIG.MUTATION_LANG.CREATE, + action: { + label: ADMIN_USERS_CRUD_CONFIG.MUTATION_LANG.VIEW_DETAILS, + action: () => router.push(NAVIGATION_LINKS.USERS.DETAILS(username)), + }, + }), }); }