F: Add global view for mutation errors

This commit is contained in:
Ivan 2025-02-19 19:26:29 +03:00
parent 60094efb3c
commit 9ef4d32376
55 changed files with 232 additions and 100 deletions

View File

@ -11,6 +11,7 @@ import { GlobalDialogs } from './GlobalDialogs';
import { GlobalLoader } from './GlobalLoader';
import { ToasterThemed } from './GlobalToaster';
import { GlobalTooltips } from './GlobalTooltips';
import { MutationErrors } from './MutationErrors';
import { Navigation } from './Navigation';
function ApplicationLayout() {
@ -48,6 +49,7 @@ function ApplicationLayout() {
>
<main className='cc-scroll-y' style={{ overflowY: showScroll ? 'scroll' : 'auto', minHeight: mainHeight }}>
<GlobalLoader />
<MutationErrors />
<Outlet />
</main>
{!noNavigation && !noFooter ? <Footer /> : null}

View File

@ -0,0 +1,40 @@
import clsx from 'clsx';
import { useMutationErrors } from '@/backend/useMutationErrors';
import { Button } from '@/components/Control';
import { DescribeError } from '@/components/InfoError';
import useEscapeKey from '@/hooks/useEscapeKey';
import { useDialogsStore } from '@/stores/dialogs';
export function MutationErrors() {
const { mutationErrors, resetErrors } = useMutationErrors();
const hideDialog = useDialogsStore(state => state.hideDialog);
useEscapeKey(resetErrors, mutationErrors.length > 0);
if (mutationErrors.length === 0) {
return null;
}
hideDialog();
return (
<div className='fixed top-0 left-0 w-full h-full z-modal cursor-default'>
<div className={clsx('z-navigation', 'fixed top-0 left-0', 'w-full h-full', 'backdrop-blur-[3px] opacity-50')} />
<div className={clsx('z-navigation', 'fixed top-0 left-0', 'w-full h-full', 'bg-prim-0 opacity-25')} />
<div
className={clsx(
'px-10 mb-10 py-3 flex flex-col items-center',
'z-modal absolute bottom-1/2 left-1/2 -translate-x-1/2 translate-y-1/2',
'border rounded-xl bg-prim-100'
)}
>
<h1 className='py-2 select-none'>Ошибка при обработке</h1>
<div className={clsx('px-3 flex flex-col', 'text-warn-600 text-sm font-semibold', 'select-text')}>
<DescribeError error={mutationErrors[0]} />
</div>
<Button onClick={resetErrors} className='w-fit' text='Закрыть' />
</div>
</div>
);
}

View File

@ -100,7 +100,6 @@ export function axiosPost<RequestData, ResponseData = void>({
} else {
toast.error(extractErrorMessage(error));
}
console.error(error);
throw error;
});
}
@ -130,7 +129,6 @@ export function axiosDelete<RequestData, ResponseData = void>({
} else {
toast.error(extractErrorMessage(error));
}
console.error(error);
throw error;
});
}
@ -160,7 +158,6 @@ export function axiosPatch<RequestData, ResponseData = void>({
} else {
toast.error(extractErrorMessage(error));
}
console.error(error);
throw error;
});
}

View File

@ -11,10 +11,12 @@ export const DELAYS = {
/** API keys for local cache. */
export const KEYS = {
oss: 'oss',
auth: 'auth',
rsform: 'rsform',
library: 'library',
users: 'users',
cctext: 'cctext',
global_mutation: 'global_mutation',
composite: {
libraryList: ['library', 'list'],

View File

@ -1,11 +1,12 @@
import { QueryClient } from '@tanstack/react-query';
import { ZodError } from 'zod';
import { AxiosError } from './apiTransport';
import { DELAYS } from './configuration';
declare module '@tanstack/react-query' {
interface Register {
defaultError: AxiosError;
defaultError: AxiosError | ZodError;
}
}

View File

@ -0,0 +1,21 @@
import { useState } from 'react';
import { useMutationState, useQueryClient } from '@tanstack/react-query';
import { KEYS } from './configuration';
export const useMutationErrors = () => {
const queryClient = useQueryClient();
const [ignored, setIgnored] = useState<(Error | null)[]>([]);
const mutationErrors = useMutationState({
filters: { mutationKey: [KEYS.global_mutation], status: 'error' },
select: mutation => mutation.state.error
});
console.log(queryClient.getMutationCache().getAll());
function resetErrors() {
setIgnored(mutationErrors);
}
return { mutationErrors: mutationErrors.filter(error => !ignored.includes(error)), resetErrors };
};

View File

@ -12,21 +12,21 @@ interface InfoErrorProps {
error: ErrorData;
}
function DescribeError({ error }: { error: ErrorData }) {
export function DescribeError({ error }: { error: ErrorData }) {
if (!error) {
return <p>Ошибки отсутствуют</p>;
} else if (typeof error === 'string') {
return <p>{error}</p>;
} else if (error instanceof ZodError) {
return (
<div className='mt-6'>
<div>
<p>Ошибка валидации данных</p>
<PrettyJson data={JSON.parse(error.toString()) as unknown} />;
</div>
);
} else if (!isAxiosError(error)) {
return (
<div className='mt-6'>
<div>
<p>
<b>Error:</b> {error.name}
</p>
@ -96,7 +96,7 @@ export function InfoError({ error }: InfoErrorProps) {
'select-text'
)}
>
<div className='font-normal clr-text-default'>
<div className='font-normal clr-text-default mb-6'>
<p>Пожалуйста сделайте скриншот и отправьте вместе с описанием ситуации на почту portal@acconcept.ru</p>
<br />
<p>Для продолжения работы перезагрузите страницу</p>

View File

@ -1,7 +1,7 @@
import { queryOptions } from '@tanstack/react-query';
import { axiosGet, axiosPatch, axiosPost } from '@/backend/apiTransport';
import { DELAYS } from '@/backend/configuration';
import { DELAYS, KEYS } from '@/backend/configuration';
import { infoMsg } from '@/utils/labels';
import {
@ -17,7 +17,7 @@ import {
* Authentication API.
*/
export const authApi = {
baseKey: 'auth',
baseKey: KEYS.auth,
getAuthQueryOptions: () => {
return queryOptions({

View File

@ -1,12 +1,14 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { KEYS } from '@/backend/configuration';
import { authApi } from './api';
import { IChangePasswordDTO } from './types';
export const useChangePassword = () => {
const client = useQueryClient();
const mutation = useMutation({
mutationKey: ['change-password'],
mutationKey: [KEYS.auth, 'change-password'],
mutationFn: authApi.changePassword,
onSettled: () => client.invalidateQueries({ queryKey: [authApi.baseKey] })
});

View File

@ -1,12 +1,14 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { KEYS } from '@/backend/configuration';
import { authApi } from './api';
import { IUserLoginDTO } from './types';
export const useLogin = () => {
const client = useQueryClient();
const mutation = useMutation({
mutationKey: ['login'],
mutationKey: [KEYS.auth, 'login'],
mutationFn: authApi.login,
onSettled: () => client.invalidateQueries({ queryKey: [authApi.baseKey] }),
onSuccess: () => client.resetQueries()

View File

@ -1,11 +1,13 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { KEYS } from '@/backend/configuration';
import { authApi } from './api';
export const useLogout = () => {
const client = useQueryClient();
const mutation = useMutation({
mutationKey: ['logout'],
mutationKey: [KEYS.auth, 'logout'],
mutationFn: authApi.logout,
onSuccess: () => client.resetQueries()
});

View File

@ -1,11 +1,13 @@
import { useMutation } from '@tanstack/react-query';
import { KEYS } from '@/backend/configuration';
import { authApi } from './api';
import { IRequestPasswordDTO } from './types';
export const useRequestPasswordReset = () => {
const mutation = useMutation({
mutationKey: ['request-password-reset'],
mutationKey: [KEYS.auth, 'request-password-reset'],
mutationFn: authApi.requestPasswordReset
});
return {

View File

@ -1,15 +1,17 @@
import { useMutation } from '@tanstack/react-query';
import { KEYS } from '@/backend/configuration';
import { authApi } from './api';
import { IPasswordTokenDTO, IResetPasswordDTO } from './types';
export const useResetPassword = () => {
const validateMutation = useMutation({
mutationKey: ['validate-token'],
mutationKey: [KEYS.auth, 'validate-token'],
mutationFn: authApi.validatePasswordToken
});
const resetMutation = useMutation({
mutationKey: ['reset-password'],
mutationKey: [KEYS.auth, 'reset-password'],
mutationFn: authApi.resetPassword
});
return {

View File

@ -1,14 +1,17 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { KEYS } from '@/backend/configuration';
import { libraryApi } from './api';
import { ICloneLibraryItemDTO } from './types';
export const useCloneItem = () => {
const client = useQueryClient();
const mutation = useMutation({
mutationKey: [libraryApi.baseKey, 'clone-item'],
mutationKey: [KEYS.global_mutation, libraryApi.baseKey, 'clone-item'],
mutationFn: libraryApi.cloneItem,
onSuccess: () => client.invalidateQueries({ queryKey: [libraryApi.baseKey] })
onSuccess: () => client.invalidateQueries({ queryKey: [libraryApi.baseKey] }),
onError: () => client.invalidateQueries()
});
return {
cloneItem: (data: ICloneLibraryItemDTO) => mutation.mutateAsync(data)

View File

@ -1,14 +1,17 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { KEYS } from '@/backend/configuration';
import { libraryApi } from './api';
import { ICreateLibraryItemDTO } from './types';
export const useCreateItem = () => {
const client = useQueryClient();
const mutation = useMutation({
mutationKey: [libraryApi.baseKey, 'create-item'],
mutationKey: [KEYS.global_mutation, libraryApi.baseKey, 'create-item'],
mutationFn: libraryApi.createItem,
onSuccess: () => client.invalidateQueries({ queryKey: [libraryApi.baseKey] })
onSuccess: () => client.invalidateQueries({ queryKey: [libraryApi.baseKey] }),
onError: () => client.invalidateQueries()
});
return {
createItem: (data: ICreateLibraryItemDTO) => mutation.mutateAsync(data),

View File

@ -8,7 +8,7 @@ import { libraryApi } from './api';
export const useDeleteItem = () => {
const client = useQueryClient();
const mutation = useMutation({
mutationKey: [libraryApi.baseKey, 'delete-item'],
mutationKey: [KEYS.global_mutation, libraryApi.baseKey, 'delete-item'],
mutationFn: libraryApi.deleteItem,
onSuccess: (_, variables) => {
client.invalidateQueries({ queryKey: libraryApi.libraryListKey }).catch(console.error);
@ -21,7 +21,8 @@ export const useDeleteItem = () => {
]).catch(console.error),
PARAMETER.navigationDuration
);
}
},
onError: () => client.invalidateQueries()
});
return {
deleteItem: (target: number) => mutation.mutateAsync(target),

View File

@ -3,8 +3,8 @@ import { useIsMutating } from '@tanstack/react-query';
import { KEYS } from '@/backend/configuration';
export const useMutatingLibrary = () => {
const countMutations = useIsMutating({ mutationKey: [KEYS.library] });
const countOss = useIsMutating({ mutationKey: [KEYS.oss] });
const countRSForm = useIsMutating({ mutationKey: [KEYS.rsform] });
const countMutations = useIsMutating({ mutationKey: [KEYS.global_mutation, KEYS.library] });
const countOss = useIsMutating({ mutationKey: [KEYS.global_mutation, KEYS.oss] });
const countRSForm = useIsMutating({ mutationKey: [KEYS.global_mutation, KEYS.rsform] });
return countMutations + countOss + countRSForm !== 0;
};

View File

@ -15,7 +15,8 @@ export const useRenameLocation = () => {
client.invalidateQueries({ queryKey: [KEYS.library] }),
client.invalidateQueries({ queryKey: [KEYS.rsform] }),
client.invalidateQueries({ queryKey: [KEYS.oss] })
])
]),
onError: () => client.invalidateQueries()
});
return {
renameLocation: (data: IRenameLocationDTO) => mutation.mutateAsync(data)

View File

@ -11,7 +11,7 @@ import { AccessPolicy, ILibraryItem } from './types';
export const useSetAccessPolicy = () => {
const client = useQueryClient();
const mutation = useMutation({
mutationKey: [libraryApi.baseKey, 'set-location'],
mutationKey: [KEYS.global_mutation, libraryApi.baseKey, 'set-location'],
mutationFn: libraryApi.setAccessPolicy,
onSuccess: (_, variables) => {
const ossKey = KEYS.composite.ossItem({ itemID: variables.itemID });
@ -39,7 +39,8 @@ export const useSetAccessPolicy = () => {
client.setQueryData(libraryApi.libraryListKey, (prev: ILibraryItem[] | undefined) =>
prev?.map(item => (item.id === variables.itemID ? { ...item, access_policy: variables.policy } : item))
);
}
},
onError: () => client.invalidateQueries()
});
return {

View File

@ -10,7 +10,7 @@ import { libraryApi } from './api';
export const useSetEditors = () => {
const client = useQueryClient();
const mutation = useMutation({
mutationKey: [libraryApi.baseKey, 'set-location'],
mutationKey: [KEYS.global_mutation, libraryApi.baseKey, 'set-location'],
mutationFn: libraryApi.setEditors,
onSuccess: (_, variables) => {
const ossKey = KEYS.composite.ossItem({ itemID: variables.itemID });
@ -34,7 +34,8 @@ export const useSetEditors = () => {
client.setQueryData(rsKey, (prev: IRSFormDTO | undefined) =>
!prev ? undefined : { ...prev, editors: variables.editors }
);
}
},
onError: () => client.invalidateQueries()
});
return {

View File

@ -11,7 +11,7 @@ import { ILibraryItem } from './types';
export const useSetLocation = () => {
const client = useQueryClient();
const mutation = useMutation({
mutationKey: [libraryApi.baseKey, 'set-location'],
mutationKey: [KEYS.global_mutation, libraryApi.baseKey, 'set-location'],
mutationFn: libraryApi.setLocation,
onSuccess: (_, variables) => {
const ossKey = KEYS.composite.ossItem({ itemID: variables.itemID });
@ -39,7 +39,8 @@ export const useSetLocation = () => {
client.setQueryData(libraryApi.libraryListKey, (prev: ILibraryItem[] | undefined) =>
prev?.map(item => (item.id === variables.itemID ? { ...item, location: variables.location } : item))
);
}
},
onError: () => client.invalidateQueries()
});
return {

View File

@ -11,7 +11,7 @@ import { ILibraryItem } from './types';
export const useSetOwner = () => {
const client = useQueryClient();
const mutation = useMutation({
mutationKey: [libraryApi.baseKey, 'set-owner'],
mutationKey: [KEYS.global_mutation, libraryApi.baseKey, 'set-owner'],
mutationFn: libraryApi.setOwner,
onSuccess: (_, variables) => {
const ossKey = KEYS.composite.ossItem({ itemID: variables.itemID });
@ -39,7 +39,8 @@ export const useSetOwner = () => {
client.setQueryData(libraryApi.libraryListKey, (prev: ILibraryItem[] | undefined) =>
prev?.map(item => (item.id === variables.itemID ? { ...item, owner: variables.owner } : item))
);
}
},
onError: () => client.invalidateQueries()
});
return {

View File

@ -11,7 +11,7 @@ import { ILibraryItem, IUpdateLibraryItemDTO, LibraryItemType } from './types';
export const useUpdateItem = () => {
const client = useQueryClient();
const mutation = useMutation({
mutationKey: [libraryApi.baseKey, 'update-item'],
mutationKey: [KEYS.global_mutation, libraryApi.baseKey, 'update-item'],
mutationFn: libraryApi.updateItem,
onSuccess: (data: ILibraryItem) => {
const itemKey =
@ -32,7 +32,8 @@ export const useUpdateItem = () => {
);
}
}
}
},
onError: () => client.invalidateQueries()
});
return {
updateItem: (data: IUpdateLibraryItemDTO) => mutation.mutateAsync(data)

View File

@ -10,12 +10,13 @@ export const useVersionCreate = () => {
const client = useQueryClient();
const { updateTimestamp } = useUpdateTimestamp();
const mutation = useMutation({
mutationKey: [libraryApi.baseKey, 'create-version'],
mutationKey: [KEYS.global_mutation, libraryApi.baseKey, 'create-version'],
mutationFn: libraryApi.versionCreate,
onSuccess: data => {
client.setQueryData(KEYS.composite.rsItem({ itemID: data.schema.id }), data.schema);
updateTimestamp(data.schema.id);
}
},
onError: () => client.invalidateQueries()
});
return {
versionCreate: (data: { itemID: number; data: IVersionCreateDTO }) =>

View File

@ -9,7 +9,7 @@ import { libraryApi } from './api';
export const useVersionDelete = () => {
const client = useQueryClient();
const mutation = useMutation({
mutationKey: [libraryApi.baseKey, 'delete-version'],
mutationKey: [KEYS.global_mutation, libraryApi.baseKey, 'delete-version'],
mutationFn: libraryApi.versionDelete,
onSuccess: (_, variables) => {
client.setQueryData(KEYS.composite.rsItem({ itemID: variables.itemID }), (prev: IRSFormDTO | undefined) =>
@ -20,7 +20,8 @@ export const useVersionDelete = () => {
versions: prev.versions.filter(version => version.id !== variables.versionID)
}
);
}
},
onError: () => client.invalidateQueries()
});
return {
versionDelete: (data: { itemID: number; versionID: number }) => mutation.mutateAsync(data)

View File

@ -7,12 +7,13 @@ import { libraryApi } from './api';
export const useVersionRestore = () => {
const client = useQueryClient();
const mutation = useMutation({
mutationKey: [libraryApi.baseKey, 'restore-version'],
mutationKey: [KEYS.global_mutation, libraryApi.baseKey, 'restore-version'],
mutationFn: libraryApi.versionRestore,
onSuccess: data => {
client.setQueryData(KEYS.composite.rsItem({ itemID: data.id }), data);
return client.invalidateQueries({ queryKey: [libraryApi.baseKey] });
}
},
onError: () => client.invalidateQueries()
});
return {
versionRestore: (data: { versionID: number }) => mutation.mutateAsync(data)

View File

@ -10,7 +10,7 @@ import { IVersionUpdateDTO } from './types';
export const useVersionUpdate = () => {
const client = useQueryClient();
const mutation = useMutation({
mutationKey: [libraryApi.baseKey, 'update-version'],
mutationKey: [KEYS.global_mutation, libraryApi.baseKey, 'update-version'],
mutationFn: libraryApi.versionUpdate,
onSuccess: (data, variables) => {
client.setQueryData(KEYS.composite.rsItem({ itemID: variables.itemID }), (prev: IRSFormDTO | undefined) =>
@ -23,7 +23,8 @@ export const useVersionUpdate = () => {
)
}
);
}
},
onError: () => client.invalidateQueries()
});
return {
versionUpdate: (data: { itemID: number; version: IVersionUpdateDTO }) => mutation.mutateAsync(data)

View File

@ -1,11 +1,13 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { ossApi } from './api';
export const useFindPredecessor = () => {
const client = useQueryClient();
const mutation = useMutation({
mutationKey: [ossApi.baseKey, 'find-predecessor'],
mutationFn: ossApi.getPredecessor
mutationFn: ossApi.getPredecessor,
onError: () => client.invalidateQueries()
});
return {
findPredecessor: (target: number) => mutation.mutateAsync({ target: target })

View File

@ -8,7 +8,7 @@ import { ITargetOperation } from './types';
export const useInputCreate = () => {
const client = useQueryClient();
const mutation = useMutation({
mutationKey: [ossApi.baseKey, 'input-create'],
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'input-create'],
mutationFn: ossApi.inputCreate,
onSuccess: data => {
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.oss.id }).queryKey, data.oss);
@ -16,7 +16,8 @@ export const useInputCreate = () => {
client.invalidateQueries({ queryKey: KEYS.composite.libraryList }),
client.invalidateQueries({ queryKey: [KEYS.rsform] })
]);
}
},
onError: () => client.invalidateQueries()
});
return {
inputCreate: (data: { itemID: number; data: ITargetOperation }) =>

View File

@ -8,7 +8,7 @@ import { IInputUpdateDTO } from './types';
export const useInputUpdate = () => {
const client = useQueryClient();
const mutation = useMutation({
mutationKey: [ossApi.baseKey, 'input-update'],
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'input-update'],
mutationFn: ossApi.inputUpdate,
onSuccess: data => {
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.id }).queryKey, data);
@ -16,7 +16,8 @@ export const useInputUpdate = () => {
client.invalidateQueries({ queryKey: KEYS.composite.libraryList }),
client.invalidateQueries({ queryKey: [KEYS.rsform] })
]);
}
},
onError: () => client.invalidateQueries()
});
return {
inputUpdate: (data: { itemID: number; data: IInputUpdateDTO }) => mutation.mutateAsync(data)

View File

@ -3,7 +3,7 @@ import { useIsMutating } from '@tanstack/react-query';
import { KEYS } from '@/backend/configuration';
export const useMutatingOss = () => {
const countLibrary = useIsMutating({ mutationKey: [KEYS.library] });
const countOss = useIsMutating({ mutationKey: [KEYS.oss] });
const countLibrary = useIsMutating({ mutationKey: [KEYS.global_mutation, KEYS.library] });
const countOss = useIsMutating({ mutationKey: [KEYS.global_mutation, KEYS.oss] });
return countLibrary + countOss !== 0;
};

View File

@ -2,6 +2,8 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useUpdateTimestamp } from '@/features/library';
import { KEYS } from '@/backend/configuration';
import { ossApi } from './api';
import { IOperationCreateDTO } from './types';
@ -9,12 +11,13 @@ export const useOperationCreate = () => {
const client = useQueryClient();
const { updateTimestamp } = useUpdateTimestamp();
const mutation = useMutation({
mutationKey: [ossApi.baseKey, 'operation-create'],
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'operation-create'],
mutationFn: ossApi.operationCreate,
onSuccess: response => {
client.setQueryData(ossApi.getOssQueryOptions({ itemID: response.oss.id }).queryKey, response.oss);
updateTimestamp(response.oss.id);
}
},
onError: () => client.invalidateQueries()
});
return {
operationCreate: (data: { itemID: number; data: IOperationCreateDTO }) => mutation.mutateAsync(data)

View File

@ -8,7 +8,7 @@ import { IOperationDeleteDTO } from './types';
export const useOperationDelete = () => {
const client = useQueryClient();
const mutation = useMutation({
mutationKey: [ossApi.baseKey, 'operation-delete'],
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'operation-delete'],
mutationFn: ossApi.operationDelete,
onSuccess: data => {
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.id }).queryKey, data);
@ -16,7 +16,8 @@ export const useOperationDelete = () => {
client.invalidateQueries({ queryKey: KEYS.composite.libraryList }),
client.invalidateQueries({ queryKey: [KEYS.rsform] })
]);
}
},
onError: () => client.invalidateQueries()
});
return {
operationDelete: (data: { itemID: number; data: IOperationDeleteDTO }) => mutation.mutateAsync(data)

View File

@ -8,7 +8,7 @@ import { ITargetOperation } from './types';
export const useOperationExecute = () => {
const client = useQueryClient();
const mutation = useMutation({
mutationKey: [ossApi.baseKey, 'operation-execute'],
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'operation-execute'],
mutationFn: ossApi.operationExecute,
onSuccess: data => {
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.id }).queryKey, data);
@ -16,7 +16,8 @@ export const useOperationExecute = () => {
client.invalidateQueries({ queryKey: KEYS.composite.libraryList }),
client.invalidateQueries({ queryKey: [KEYS.rsform] })
]);
}
},
onError: () => client.invalidateQueries()
});
return {
operationExecute: (data: { itemID: number; data: ITargetOperation }) => mutation.mutateAsync(data)

View File

@ -10,7 +10,7 @@ import { IOperationUpdateDTO } from './types';
export const useOperationUpdate = () => {
const client = useQueryClient();
const mutation = useMutation({
mutationKey: [ossApi.baseKey, 'operation-update'],
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'operation-update'],
mutationFn: ossApi.operationUpdate,
onSuccess: (data, variables) => {
client.setQueryData(KEYS.composite.ossItem({ itemID: data.id }), data);
@ -28,7 +28,8 @@ export const useOperationUpdate = () => {
return client.invalidateQueries({
queryKey: KEYS.composite.rsItem({ itemID: schemaID })
});
}
},
onError: () => client.invalidateQueries()
});
return {
operationUpdate: (data: { itemID: number; data: IOperationUpdateDTO }) => mutation.mutateAsync(data)

View File

@ -8,7 +8,7 @@ import { ICstRelocateDTO } from './types';
export const useRelocateConstituents = () => {
const client = useQueryClient();
const mutation = useMutation({
mutationKey: [ossApi.baseKey, 'relocate-constituents'],
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'relocate-constituents'],
mutationFn: ossApi.relocateConstituents,
onSuccess: data => {
client.setQueryData(ossApi.getOssQueryOptions({ itemID: data.id }).queryKey, data);
@ -16,7 +16,8 @@ export const useRelocateConstituents = () => {
client.invalidateQueries({ queryKey: KEYS.composite.libraryList }),
client.invalidateQueries({ queryKey: [KEYS.rsform] })
]);
}
},
onError: () => client.invalidateQueries()
});
return {
relocateConstituents: (data: ICstRelocateDTO) => mutation.mutateAsync(data)

View File

@ -1,16 +1,20 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useUpdateTimestamp } from '@/features/library';
import { KEYS } from '@/backend/configuration';
import { ossApi } from './api';
import { IOperationPosition } from './types';
export const useUpdatePositions = () => {
const client = useQueryClient();
const { updateTimestamp } = useUpdateTimestamp();
const mutation = useMutation({
mutationKey: [ossApi.baseKey, 'update-positions'],
mutationKey: [KEYS.global_mutation, ossApi.baseKey, 'update-positions'],
mutationFn: ossApi.updatePositions,
onSuccess: (_, variables) => updateTimestamp(variables.itemID)
onSuccess: (_, variables) => updateTimestamp(variables.itemID),
onError: () => client.invalidateQueries()
});
return {
updatePositions: (data: {

View File

@ -1,11 +1,13 @@
import { useMutation } from '@tanstack/react-query';
import { KEYS } from '@/backend/configuration';
import { rsformsApi } from './api';
import { ICheckConstituentaDTO } from './types';
export const useCheckConstituenta = () => {
const mutation = useMutation({
mutationKey: ['actions', 'check-constituenta'],
mutationKey: [KEYS.global_mutation, 'actions', 'check-constituenta'],
mutationFn: rsformsApi.checkConstituenta
});
return {

View File

@ -11,7 +11,7 @@ export const useCstCreate = () => {
const client = useQueryClient();
const { updateTimestamp } = useUpdateTimestamp();
const mutation = useMutation({
mutationKey: [rsformsApi.baseKey, 'create-cst'],
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'create-cst'],
mutationFn: rsformsApi.cstCreate,
onSuccess: data => {
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.schema.id }).queryKey, data.schema);
@ -24,7 +24,8 @@ export const useCstCreate = () => {
predicate: query => query.queryKey.length > 2 && query.queryKey[2] !== data.schema.id
})
]);
}
},
onError: () => client.invalidateQueries()
});
return {
cstCreate: (data: { itemID: number; data: ICstCreateDTO }) =>

View File

@ -11,7 +11,7 @@ export const useCstDelete = () => {
const client = useQueryClient();
const { updateTimestamp } = useUpdateTimestamp();
const mutation = useMutation({
mutationKey: [rsformsApi.baseKey, 'delete-multiple-cst'],
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'delete-multiple-cst'],
mutationFn: rsformsApi.cstDelete,
onSuccess: data => {
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data);
@ -24,7 +24,8 @@ export const useCstDelete = () => {
predicate: query => query.queryKey.length > 2 && query.queryKey[2] !== data.id
})
]);
}
},
onError: () => client.invalidateQueries()
});
return {
cstDelete: (data: { itemID: number; data: IConstituentaList }) => mutation.mutateAsync(data)

View File

@ -2,6 +2,8 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useUpdateTimestamp } from '@/features/library';
import { KEYS } from '@/backend/configuration';
import { rsformsApi } from './api';
import { ICstMoveDTO } from './types';
@ -9,12 +11,13 @@ export const useCstMove = () => {
const client = useQueryClient();
const { updateTimestamp } = useUpdateTimestamp();
const mutation = useMutation({
mutationKey: [rsformsApi.baseKey, 'move-cst'],
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'move-cst'],
mutationFn: rsformsApi.cstMove,
onSuccess: data => {
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data);
updateTimestamp(data.id);
}
},
onError: () => client.invalidateQueries()
});
return {
cstMove: (data: { itemID: number; data: ICstMoveDTO }) => mutation.mutateAsync(data)

View File

@ -11,7 +11,7 @@ export const useCstRename = () => {
const client = useQueryClient();
const { updateTimestamp } = useUpdateTimestamp();
const mutation = useMutation({
mutationKey: [rsformsApi.baseKey, 'rename-cst'],
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'rename-cst'],
mutationFn: rsformsApi.cstRename,
onSuccess: data => {
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.schema.id }).queryKey, data.schema);
@ -24,7 +24,8 @@ export const useCstRename = () => {
predicate: query => query.queryKey.length > 2 && query.queryKey[2] !== data.schema.id
})
]);
}
},
onError: () => client.invalidateQueries()
});
return {
cstRename: (data: { itemID: number; data: ICstRenameDTO }) => mutation.mutateAsync(data)

View File

@ -11,7 +11,7 @@ export const useCstSubstitute = () => {
const client = useQueryClient();
const { updateTimestamp } = useUpdateTimestamp();
const mutation = useMutation({
mutationKey: [rsformsApi.baseKey, 'substitute-cst'],
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'substitute-cst'],
mutationFn: rsformsApi.cstSubstitute,
onSuccess: data => {
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data);
@ -24,7 +24,8 @@ export const useCstSubstitute = () => {
predicate: query => query.queryKey.length > 2 && query.queryKey[2] !== data.id
})
]);
}
},
onError: () => client.invalidateQueries()
});
return {
cstSubstitute: (data: { itemID: number; data: ICstSubstitutionsDTO }) => mutation.mutateAsync(data)

View File

@ -11,7 +11,7 @@ export const useCstUpdate = () => {
const client = useQueryClient();
const { updateTimestamp } = useUpdateTimestamp();
const mutation = useMutation({
mutationKey: [rsformsApi.baseKey, 'update-cst'],
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'update-cst'],
mutationFn: rsformsApi.cstUpdate,
onSuccess: (_, variables) => {
updateTimestamp(variables.itemID);
@ -20,7 +20,8 @@ export const useCstUpdate = () => {
client.invalidateQueries({ queryKey: [KEYS.oss] }),
client.invalidateQueries({ queryKey: [rsformsApi.baseKey] })
]);
}
},
onError: () => client.invalidateQueries()
});
return {
cstUpdate: (data: { itemID: number; data: ICstUpdateDTO }) => mutation.mutateAsync(data)

View File

@ -1,10 +1,12 @@
import { useMutation } from '@tanstack/react-query';
import { KEYS } from '@/backend/configuration';
import { rsformsApi } from './api';
export const useDownloadRSForm = () => {
const mutation = useMutation({
mutationKey: [rsformsApi.baseKey, 'download'],
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'download'],
mutationFn: rsformsApi.download
});
return {

View File

@ -11,7 +11,7 @@ export const useInlineSynthesis = () => {
const client = useQueryClient();
const { updateTimestamp } = useUpdateTimestamp();
const mutation = useMutation({
mutationKey: [rsformsApi.baseKey, 'inline-synthesis'],
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'inline-synthesis'],
mutationFn: rsformsApi.inlineSynthesis,
onSuccess: data => {
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data);
@ -24,7 +24,8 @@ export const useInlineSynthesis = () => {
predicate: query => query.queryKey.length > 2 && query.queryKey[2] !== data.id
})
]);
}
},
onError: () => client.invalidateQueries()
});
return {
inlineSynthesis: (data: IInlineSynthesisDTO) => mutation.mutateAsync(data)

View File

@ -3,7 +3,7 @@ import { useIsMutating } from '@tanstack/react-query';
import { KEYS } from '@/backend/configuration';
export const useMutatingRSForm = () => {
const countLibrary = useIsMutating({ mutationKey: [KEYS.library] });
const countRsform = useIsMutating({ mutationKey: [KEYS.rsform] });
const countLibrary = useIsMutating({ mutationKey: [KEYS.global_mutation, KEYS.library] });
const countRsform = useIsMutating({ mutationKey: [KEYS.global_mutation, KEYS.rsform] });
return countLibrary + countRsform !== 0;
};

View File

@ -11,7 +11,7 @@ export const useProduceStructure = () => {
const client = useQueryClient();
const { updateTimestamp } = useUpdateTimestamp();
const mutation = useMutation({
mutationKey: [rsformsApi.baseKey, 'produce-structure'],
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'produce-structure'],
mutationFn: rsformsApi.produceStructure,
onSuccess: data => {
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.schema.id }).queryKey, data.schema);
@ -24,7 +24,8 @@ export const useProduceStructure = () => {
predicate: query => query.queryKey.length > 2 && query.queryKey[2] !== data.schema.id
})
]);
}
},
onError: () => client.invalidateQueries()
});
return {
produceStructure: (data: { itemID: number; data: ITargetCst }) =>

View File

@ -10,7 +10,7 @@ export const useResetAliases = () => {
const client = useQueryClient();
const { updateTimestamp } = useUpdateTimestamp();
const mutation = useMutation({
mutationKey: [rsformsApi.baseKey, 'reset-aliases'],
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'reset-aliases'],
mutationFn: rsformsApi.resetAliases,
onSuccess: data => {
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data);
@ -23,7 +23,8 @@ export const useResetAliases = () => {
predicate: query => query.queryKey.length > 2 && query.queryKey[2] !== data.id
})
]);
}
},
onError: () => client.invalidateQueries()
});
return {
resetAliases: (data: { itemID: number }) => mutation.mutateAsync(data)

View File

@ -2,18 +2,21 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useUpdateTimestamp } from '@/features/library';
import { KEYS } from '@/backend/configuration';
import { rsformsApi } from './api';
export const useRestoreOrder = () => {
const client = useQueryClient();
const { updateTimestamp } = useUpdateTimestamp();
const mutation = useMutation({
mutationKey: [rsformsApi.baseKey, 'restore-order'],
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'restore-order'],
mutationFn: rsformsApi.restoreOrder,
onSuccess: data => {
client.setQueryData(rsformsApi.getRSFormQueryOptions({ itemID: data.id }).queryKey, data);
updateTimestamp(data.id);
}
},
onError: () => client.invalidateQueries()
});
return {
restoreOrder: (data: { itemID: number }) => mutation.mutateAsync(data)

View File

@ -10,7 +10,7 @@ import { IRSFormUploadDTO } from './types';
export const useUploadTRS = () => {
const client = useQueryClient();
const mutation = useMutation({
mutationKey: [rsformsApi.baseKey, 'load-trs'],
mutationKey: [KEYS.global_mutation, rsformsApi.baseKey, 'load-trs'],
mutationFn: rsformsApi.upload,
onSuccess: data => {
client.setQueryData(KEYS.composite.rsItem({ itemID: data.id }), data);
@ -25,7 +25,8 @@ export const useUploadTRS = () => {
predicate: query => query.queryKey.length > 2 && query.queryKey[2] !== data.id
})
]);
}
},
onError: () => client.invalidateQueries()
});
return {
upload: (data: IRSFormUploadDTO) => mutation.mutateAsync(data)

View File

@ -10,6 +10,7 @@ import { IUpdateProfileDTO, IUserSignupDTO } from './types';
export const usersApi = {
baseKey: KEYS.users,
getUsersQueryOptions: () =>
queryOptions({
queryKey: [usersApi.baseKey, 'list'],

View File

@ -1,12 +1,14 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { KEYS } from '@/backend/configuration';
import { usersApi } from './api';
import { IUserSignupDTO } from './types';
export const useSignup = () => {
const client = useQueryClient();
const mutation = useMutation({
mutationKey: ['signup'],
mutationKey: [KEYS.users, 'signup'],
mutationFn: usersApi.signup,
onSuccess: () => client.invalidateQueries({ queryKey: usersApi.getUsersQueryOptions().queryKey })
});

View File

@ -1,17 +1,20 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { KEYS } from '@/backend/configuration';
import { usersApi } from './api';
import { IUpdateProfileDTO } from './types';
export const useUpdateProfile = () => {
const client = useQueryClient();
const mutation = useMutation({
mutationKey: ['update-profile'],
mutationKey: [KEYS.users, 'update-profile'],
mutationFn: usersApi.updateProfile,
onSuccess: data => {
client.setQueryData(usersApi.getProfileQueryOptions().queryKey, data);
return client.invalidateQueries({ queryKey: usersApi.getUsersQueryOptions().queryKey });
}
},
onError: () => client.invalidateQueries()
});
return {
updateProfile: (data: IUpdateProfileDTO) => mutation.mutateAsync(data),

View File

@ -2,7 +2,7 @@
import { useCallback, useEffect } from 'react';
function useEscapeKey(handleClose: () => void) {
function useEscapeKey(handleClose: () => void, isEnabled: boolean = true) {
const handleEscKey = useCallback(
(event: KeyboardEvent) => {
if (event.key === 'Escape') {
@ -13,11 +13,13 @@ function useEscapeKey(handleClose: () => void) {
);
useEffect(() => {
document.addEventListener('keyup', handleEscKey, false);
return () => {
document.removeEventListener('keyup', handleEscKey, false);
};
}, [handleEscKey]);
if (isEnabled) {
document.addEventListener('keyup', handleEscKey, false);
return () => {
document.removeEventListener('keyup', handleEscKey, false);
};
}
}, [handleEscKey, isEnabled]);
}
export default useEscapeKey;