UNPKG

@refinedev/core

Version:

refine is a React-based framework for building internal tools, rapidly. It ships with Ant Design System, an enterprise-level UI toolkit.

342 lines (311 loc) 10 kB
import { getXRay } from "@refinedev/devtools-internal"; import { type UseMutationOptions, type MutateOptions, useMutation, } from "@tanstack/react-query"; import { handleMultiple, pickDataProvider, pickNotDeprecated, } from "@definitions"; import { useDataProvider, useHandleNotification, useInvalidate, useKeys, useLog, useMeta, usePublish, useRefineContext, useResource, useTranslate, } from "@hooks"; import type { BaseRecord, CreateManyResponse, HttpError, IQueryKeys, MetaQuery, } from "../../contexts/data/types"; import type { UseMutationResult } from "../../definitions/types"; import type { SuccessErrorNotification } from "../../contexts/notification/types"; import { type UseLoadingOvertimeOptionsProps, type UseLoadingOvertimeReturnType, useLoadingOvertime, } from "../useLoadingOvertime"; export type UseCreateManyParams<TData, TError, TVariables> = { resource?: string; values?: TVariables[]; meta?: MetaQuery; metaData?: MetaQuery; dataProviderName?: string; invalidates?: Array<keyof IQueryKeys>; } & SuccessErrorNotification<CreateManyResponse<TData>, TError, TVariables[]>; export type UseCreateManyReturnType< TData extends BaseRecord = BaseRecord, TError = HttpError, TVariables = {}, > = UseMutationResult< CreateManyResponse<TData>, TError, UseCreateManyParams<TData, TError, TVariables>, unknown >; export type UseCreateManyProps< TData extends BaseRecord = BaseRecord, TError extends HttpError = HttpError, TVariables = {}, > = { mutationOptions?: Omit< UseMutationOptions< CreateManyResponse<TData>, TError, UseCreateManyParams<TData, TError, TVariables> >, "mutationFn" >; } & UseLoadingOvertimeOptionsProps & UseCreateManyParams<TData, TError, TVariables>; /** * `useCreateMany` is a modified version of `react-query`'s {@link https://react-query.tanstack.com/reference/useMutation `useMutation`} for multiple create mutations. * * It uses `createMany` method as mutation function from the `dataProvider` which is passed to `<Refine>`. * * @see {@link https://refine.dev/docs/api-reference/core/hooks/data/useCreateMany} for more details. * * @typeParam TData - Result data of the query extends {@link https://refine.dev/docs/core/interfaceReferences#baserecord `BaseRecord`} * @typeParam TError - Custom error object that extends {@link https://refine.dev/docs/core/interfaceReferences#httperror `HttpError`} * @typeParam TVariables - Values for mutation function * */ export const useCreateMany = < TData extends BaseRecord = BaseRecord, TError extends HttpError = HttpError, TVariables = {}, >({ resource: resourceFromProps, values: valuesFromProps, dataProviderName: dataProviderNameFromProps, successNotification: successNotificationFromProps, errorNotification: errorNotificationFromProps, meta: metaFromProps, metaData: metaDataFromProps, invalidates: invalidatesFromProps, mutationOptions, overtimeOptions, }: UseCreateManyProps<TData, TError, TVariables> = {}): UseCreateManyReturnType< TData, TError, TVariables > & UseLoadingOvertimeReturnType => { const dataProvider = useDataProvider(); const { resources, select } = useResource(); const translate = useTranslate(); const publish = usePublish(); const handleNotification = useHandleNotification(); const invalidateStore = useInvalidate(); const { log } = useLog(); const getMeta = useMeta(); const { options: { textTransformers }, } = useRefineContext(); const { keys, preferLegacyKeys } = useKeys(); const mutationResult = useMutation< CreateManyResponse<TData>, TError, UseCreateManyParams<TData, TError, TVariables> >({ mutationFn: ({ resource: resourceName = resourceFromProps, values = valuesFromProps, meta = metaFromProps, metaData = metaDataFromProps, dataProviderName = dataProviderNameFromProps, }: UseCreateManyParams<TData, TError, TVariables>) => { if (!values) throw missingValuesError; if (!resourceName) throw missingResourceError; const { resource, identifier } = select(resourceName); const combinedMeta = getMeta({ resource, meta: pickNotDeprecated(meta, metaData), }); const selectedDataProvider = dataProvider( pickDataProvider(identifier, dataProviderName, resources), ); if (selectedDataProvider.createMany) { return selectedDataProvider.createMany<TData, TVariables>({ resource: resource.name, variables: values, meta: combinedMeta, metaData: combinedMeta, }); } return handleMultiple( values.map((val) => selectedDataProvider.create<TData, TVariables>({ resource: resource.name, variables: val, meta: combinedMeta, metaData: combinedMeta, }), ), ); }, onSuccess: (response, variables, context) => { const { resource: resourceName = resourceFromProps, successNotification = successNotificationFromProps, dataProviderName: dataProviderNameFromProp = dataProviderNameFromProps, invalidates = invalidatesFromProps ?? ["list", "many"], values = valuesFromProps, meta = metaFromProps, metaData = metaDataFromProps, } = variables; if (!values) throw missingValuesError; if (!resourceName) throw missingResourceError; const { resource, identifier } = select(resourceName); const resourcePlural = textTransformers.plural(identifier); const dataProviderName = pickDataProvider( identifier, dataProviderNameFromProp, resources, ); const combinedMeta = getMeta({ resource, meta: pickNotDeprecated(meta, metaData), }); const notificationConfig = typeof successNotification === "function" ? successNotification(response, values, identifier) : successNotification; handleNotification(notificationConfig, { key: `createMany-${identifier}-notification`, message: translate( "notifications.createSuccess", { resource: translate(`${identifier}.${identifier}`, identifier), }, `Successfully created ${resourcePlural}`, ), description: translate("notifications.success", "Success"), type: "success", }); invalidateStore({ resource: identifier, dataProviderName, invalidates, }); const ids = response?.data .filter((item) => item?.id !== undefined) .map((item) => item.id!); publish?.({ channel: `resources/${resource.name}`, type: "created", payload: { ids, }, date: new Date(), meta: { ...combinedMeta, dataProviderName, }, }); const { fields: _fields, operation: _operation, variables: _variables, ...rest } = combinedMeta || {}; log?.mutate({ action: "createMany", resource: resource.name, data: values, meta: { dataProviderName, ids, ...rest, }, }); mutationOptions?.onSuccess?.(response, variables, context); }, onError: (err: TError, variables, context) => { const { resource: resourceName = resourceFromProps, errorNotification = errorNotificationFromProps, values = valuesFromProps, } = variables; if (!values) throw missingValuesError; if (!resourceName) throw missingResourceError; const { identifier } = select(resourceName); const notificationConfig = typeof errorNotification === "function" ? errorNotification(err, values, identifier) : errorNotification; handleNotification(notificationConfig, { key: `createMany-${identifier}-notification`, description: err.message, message: translate( "notifications.createError", { resource: translate(`${identifier}.${identifier}`, identifier), statusCode: err.statusCode, }, `There was an error creating ${identifier} (status code: ${err.statusCode}`, ), type: "error", }); mutationOptions?.onError?.(err, variables, context); }, mutationKey: keys().data().mutation("createMany").get(preferLegacyKeys), ...mutationOptions, meta: { ...mutationOptions?.meta, ...getXRay("useCreateMany", preferLegacyKeys), }, }); const { mutate, mutateAsync, ...mutation } = mutationResult; const { elapsedTime } = useLoadingOvertime({ isLoading: mutation.isLoading, interval: overtimeOptions?.interval, onInterval: overtimeOptions?.onInterval, }); // this function is used to make the `variables` parameter optional const handleMutation = ( variables?: UseCreateManyParams<TData, TError, TVariables>, options?: MutateOptions< CreateManyResponse<TData>, TError, UseCreateManyParams<TData, TError, TVariables>, unknown >, ) => { return mutate(variables || {}, options); }; // this function is used to make the `variables` parameter optional const handleMutateAsync = ( variables?: UseCreateManyParams<TData, TError, TVariables>, options?: MutateOptions< CreateManyResponse<TData>, TError, UseCreateManyParams<TData, TError, TVariables>, unknown >, ) => { return mutateAsync(variables || {}, options); }; return { ...mutation, mutate: handleMutation, mutateAsync: handleMutateAsync, overtime: { elapsedTime }, }; }; const missingResourceError = new Error( "[useCreateMany]: `resource` is not defined or not matched but is required", ); const missingValuesError = new Error( "[useCreateMany]: `values` is not provided but is required", );