import React from "react"; import { useMutation, UseMutationOptions, useQueryClient, } from "@tanstack/react-query"; import { AxiosError } from "axios"; import { Text } from "@mantine/core"; import { modals } from "@mantine/modals"; import { HttpValidationError } from "@/lib/client"; import { notifications } from "@/lib/notifications"; import { sortByLexorank } from "@/utils/lexorank"; import { BaseEntity, CreateMutationOptions, DeleteMutationOptions, UpdateMutationOptions, } from "./types"; type CrudOperations = { onCreate: (name: string) => void; onUpdate: (id: number, update: TUpdate) => void; onDelete: (entity: TEntity) => void; }; type UseEntityOperationsProps = { key: string; queryKey: any[]; mutations: { create: UseMutationOptions< any, AxiosError, CreateMutationOptions >; update: UseMutationOptions< any, AxiosError, UpdateMutationOptions >; delete: UseMutationOptions< any, AxiosError, DeleteMutationOptions >; }; getCreateEntity: (name: string) => TCreate | null; getUpdateEntity: (oldEntity: TEntity, update: TUpdate) => TEntity; getDeleteConfirmTitle: (entity: TEntity) => string; }; const useCrudOperations = < TEntity extends BaseEntity, TUpdate extends object, TCreate extends object, >({ key, queryKey, mutations, getCreateEntity, getUpdateEntity, getDeleteConfirmTitle, }: UseEntityOperationsProps): CrudOperations< TEntity, TUpdate > => { const queryClient = useQueryClient(); const onError = ( error: AxiosError, _: any, context: any ) => { console.error(error); notifications.error({ message: error.response?.data?.detail as string | undefined, }); if (context?.previous) { queryClient.setQueryData(queryKey, context.previous); } }; const onSettled = () => { queryClient.invalidateQueries({ predicate: (query: { queryKey: any }) => query.queryKey[0]?._id === key, }); }; const createMutation = useMutation({ ...mutations.create, onError, onSettled, }); const updateMutation = useMutation({ ...mutations.update, onError, onSettled, onMutate: async ({ body: { entity: update } }) => { await queryClient.cancelQueries({ queryKey: [key] }); const previous = queryClient.getQueryData(queryKey); queryClient.setQueryData(queryKey, (old: { items: TEntity[] }) => { let updated = old.items.map((entity: TEntity) => entity.id === update.id ? getUpdateEntity(entity, update) : entity ); if ("lexorank" in update) { updated = sortByLexorank( updated as (TEntity & { lexorank: string })[] ); } return { ...old, items: updated, }; }); return { previous }; }, }); const deleteMutation = useMutation({ ...mutations.delete, onError, onSettled, onMutate: async ({ path: { pk } }) => { await queryClient.cancelQueries({ queryKey: [key] }); const previous = queryClient.getQueryData(queryKey); queryClient.setQueryData(queryKey, (old: { items: TEntity[] }) => { const filtered = old.items.filter(e => e.id !== pk); return { ...old, items: filtered, }; }); return { previous }; }, }); const onCreate = (name: string) => { const entity = getCreateEntity(name); if (!entity) return; createMutation.mutate({ body: { entity, }, path: undefined, query: undefined, }); }; const onUpdate = async (id: number, update: TUpdate) => { updateMutation.mutate({ body: { entity: update, }, path: { pk: id }, query: undefined, }); }; const onDelete = (entity: TEntity, onSuccess?: () => void) => { modals.openConfirmModal({ title: getDeleteConfirmTitle(entity), children: ( Вы уверены, что хотите удалить "{entity.name}"? ), confirmProps: { color: "red" }, onConfirm: () => { deleteMutation.mutate({ path: { pk: entity.id } } as any); onSuccess && onSuccess(); }, }); }; return { onCreate, onUpdate, onDelete }; }; export default useCrudOperations;