import { useMutation, useQuery, useQueryClient, type DefinedInitialDataOptions, type UseQueryResult, } from "@tanstack/react-query"; import { Config } from "@/config/config"; import type { Form, FormCreate, FormEditPayload } from "@/services/resources/forms"; import type { Shipment, ShipmentCreate, ShipmentEditPayload } from "@/services/resources/shipments"; import type { Productor, ProductorCreate, ProductorEditPayload, } from "@/services/resources/productors"; import type { Role, User, UserCreate, UserEditPayload, UserLogged, } from "@/services/resources/users"; import type { Product, ProductCreate, ProductEditPayload } from "./resources/products"; import type { Contract, ContractCreate } from "./resources/contracts"; import { notifications } from "@mantine/notifications"; import { t } from "@/config/i18n"; import type { DeleteDependencies, EntityName } from "./resources/common"; export async function refreshToken() { return await fetch(`${Config.backend_uri}/auth/refresh`, {method: "POST", credentials: "include"}); } export async function fetchWithAuth(input: RequestInfo, options?: RequestInit, redirect: boolean = true) { const res = await fetch(input, { credentials: "include", ...options, }); if (res.status === 401) { const refresh = await refreshToken(); if (refresh.status !== 200) { if (redirect) window.location.href = `/?sessionExpired=True`; const error = new Error("Unauthorized"); error.cause = 401 throw error; } const newRes = await fetch(input, { credentials: "include", ...options, }); if (newRes.status === 401 || newRes.status === 403) { if (redirect) window.location.href = `/?sessionExpired=True`; const error = new Error("Unauthorized"); error.cause = 401 throw error; } return newRes; } if (res.status == 403) { throw new Error(res.statusText); } return res; } export function useGetShipments(filters?: URLSearchParams): UseQueryResult { const queryString = filters?.toString(); return useQuery({ queryKey: ["shipments", queryString], queryFn: () => fetchWithAuth(`${Config.backend_uri}/shipments${filters ? `?${queryString}` : ""}`, { credentials: "include", }).then((res) => res.json()), }); } export function useGetShipment( id?: number, options?: Partial>, ): UseQueryResult { return useQuery({ queryKey: ["shipment"], queryFn: () => fetchWithAuth(`${Config.backend_uri}/shipments/${id}`, { credentials: "include", }).then((res) => res.json()), enabled: !!id, ...options, }); } export function useCreateShipment() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (newShipment: ShipmentCreate) => { return fetchWithAuth(`${Config.backend_uri}/shipments`, { method: "POST", credentials: "include", headers: { "Content-Type": "application/json", }, body: JSON.stringify(newShipment), }).then((res) => res.json()); }, onSuccess: async () => { notifications.show({ title: t("success", { capfirst: true }), message: t("success create", { capfirst: true, entity: t("shipment") }), }); await queryClient.invalidateQueries({ queryKey: ["shipments"] }); }, onError: (error) => { notifications.show({ title: t("error", { capfirst: true }), message: error?.message || t(`error create`, { capfirst: true, entity: t("of the shipment") }), color: "red", }); }, }); } export function useEditShipment() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ shipment, id }: ShipmentEditPayload) => { return fetchWithAuth(`${Config.backend_uri}/shipments/${id}`, { method: "PUT", credentials: "include", headers: { "Content-Type": "application/json", }, body: JSON.stringify(shipment), }).then((res) => res.json()); }, onSuccess: async () => { notifications.show({ title: t("success", { capfirst: true }), message: t("success edit", { capfirst: true, entity: t("shipment") }), }); await queryClient.invalidateQueries({ queryKey: ["shipments"] }); }, onError: (error) => { notifications.show({ title: t("error", { capfirst: true }), message: error?.message || t(`error edit`, { capfirst: true, entity: t("of the shipment") }), color: "red", }); }, }); } export function useDeleteShipment() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (id: number) => { return fetchWithAuth(`${Config.backend_uri}/shipments/${id}`, { method: "DELETE", credentials: "include", headers: { "Content-Type": "application/json", }, }).then((res) => res.json()); }, onSuccess: async () => { notifications.show({ title: t("success", { capfirst: true }), message: t("success delete", { capfirst: true, entity: t("shipment") }), }); await queryClient.invalidateQueries({ queryKey: ["shipments"] }); }, onError: (error) => { notifications.show({ title: t("error", { capfirst: true }), message: error?.message || t(`error delete`, { capfirst: true, entity: t("of the contract") }), color: "red", }); }, }); } export function useGetProductors(filters?: URLSearchParams): UseQueryResult { const queryString = filters?.toString(); return useQuery({ queryKey: ["productors", queryString], queryFn: () => fetchWithAuth(`${Config.backend_uri}/productors${filters ? `?${queryString}` : ""}`, { credentials: "include", }) .then((res) => res.json() ), }); } export function useGetProductor( id?: number, options?: Partial>, ) { return useQuery({ queryKey: ["productor"], queryFn: () => fetchWithAuth(`${Config.backend_uri}/productors/${id}`, { credentials: "include", }).then((res) => res.json()), enabled: !!id, ...options, }); } export function useCreateProductor() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (newProductor: ProductorCreate) => { return fetchWithAuth(`${Config.backend_uri}/productors`, { method: "POST", credentials: "include", headers: { "Content-Type": "application/json", }, body: JSON.stringify(newProductor), }).then((res) => res.json()); }, onSuccess: async () => { notifications.show({ title: t("success", { capfirst: true }), message: t("success create", { capfirst: true, entity: t("productor") }), }); await queryClient.invalidateQueries({ queryKey: ["productors"] }); }, onError: (error) => { notifications.show({ title: t("error", { capfirst: true }), message: error?.message || t(`error create`, { capfirst: true, entity: t("of the productor") }), color: "red", }); }, }); } export function useEditProductor() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ productor, id }: ProductorEditPayload) => { return fetchWithAuth(`${Config.backend_uri}/productors/${id}`, { method: "PUT", credentials: "include", headers: { "Content-Type": "application/json", }, body: JSON.stringify(productor), }).then((res) => res.json()); }, onSuccess: async () => { notifications.show({ title: t("success", { capfirst: true }), message: t("success edit", { capfirst: true, entity: t("productor") }), }); await queryClient.invalidateQueries({ queryKey: ["productors"] }); }, onError: (error) => { notifications.show({ title: t("error", { capfirst: true }), message: error?.message || t(`error edit`, { capfirst: true, entity: t("of the productor") }), color: "red", }); }, }); } export function useDeleteProductor() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (id: number) => { return fetchWithAuth(`${Config.backend_uri}/productors/${id}`, { method: "DELETE", credentials: "include", headers: { "Content-Type": "application/json", }, }).then((res) => res.json()); }, onSuccess: async () => { notifications.show({ title: t("success", { capfirst: true }), message: t("success delete", { capfirst: true, entity: t("productor") }), }); await queryClient.invalidateQueries({ queryKey: ["productors"] }); }, onError: (error) => { notifications.show({ title: t("error", { capfirst: true }), message: error?.message || t(`error delete`, { capfirst: true, entity: t("of the productor") }), color: "red", }); }, }); } export function useGetForm( id?: number, options?: Partial>, ) { return useQuery
({ queryKey: ["form"], queryFn: () => fetchWithAuth(`${Config.backend_uri}/forms/${id}`, { credentials: "include", }).then((res) => res.json()), enabled: !!id, ...options, }); } export function useGetDeleteDependencies( entity: EntityName, id?: number, ) { return useQuery({ queryKey: [`${entity}_delete_preview_${id}`], queryFn: () => fetchWithAuth(`${Config.backend_uri}/${entity}s/${id}/preview-delete`, { credentials: "include", }).then((res) => { const result = res.json() return result }), enabled: !!id, }); } export function useGetForms(filters?: URLSearchParams): UseQueryResult { const queryString = filters?.toString(); return useQuery({ queryKey: ["forms", queryString], queryFn: () => fetch(`${Config.backend_uri}/forms${filters ? `?${queryString}` : ""}`).then((res) => res.json()), }); } export function useGetReferentForms(filters?: URLSearchParams): UseQueryResult { const queryString = filters?.toString(); return useQuery({ queryKey: ["forms", queryString], queryFn: () => fetchWithAuth(`${Config.backend_uri}/forms/referents${filters ? `?${queryString}` : ""}`).then((res) => res.json()), }); } export function useCreateForm() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (newForm: FormCreate) => { return fetchWithAuth(`${Config.backend_uri}/forms`, { method: "POST", credentials: "include", headers: { "Content-Type": "application/json", }, body: JSON.stringify(newForm), }).then((res) => res.json()); }, onSuccess: async () => { await queryClient.invalidateQueries({ queryKey: ["forms"] }); }, }); } export function useDeleteForm() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (id: number) => { return fetchWithAuth(`${Config.backend_uri}/forms/${id}`, { method: "DELETE", credentials: "include", headers: { "Content-Type": "application/json", }, }).then((res) => res.json()); }, onSuccess: async () => { notifications.show({ title: t("success", { capfirst: true }), message: t("success delete", { capfirst: true, entity: t("form") }), }); await queryClient.invalidateQueries({ queryKey: ["forms"] }); }, onError: (error) => { notifications.show({ title: t("error", { capfirst: true }), message: error?.message || t(`error delete`, { capfirst: true, entity: t("of the form") }), color: "red", }); }, }); } export function useEditForm() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ id, form }: FormEditPayload) => { return fetchWithAuth(`${Config.backend_uri}/forms/${id}`, { method: "PUT", credentials: "include", headers: { "Content-Type": "application/json", }, body: JSON.stringify(form), }).then((res) => res.json()); }, onSuccess: async () => { notifications.show({ title: t("success", { capfirst: true }), message: t("success edit", { capfirst: true, entity: t("form") }), }); await queryClient.invalidateQueries({ queryKey: ["forms"] }); }, onError: (error) => { notifications.show({ title: t("error", { capfirst: true }), message: error?.message || t(`error edit`, { capfirst: true, entity: t("of the form") }), color: "red", }); }, }); } export function useGetProduct( id?: number, options?: Partial>, ) { return useQuery({ queryKey: ["product"], queryFn: () => fetchWithAuth(`${Config.backend_uri}/products/${id}`, { credentials: "include", }).then((res) => res.json()), enabled: !!id, ...options, }); } export function useGetProducts(filters?: URLSearchParams): UseQueryResult { const queryString = filters?.toString(); return useQuery({ queryKey: ["products", queryString], queryFn: () => fetchWithAuth(`${Config.backend_uri}/products${filters ? `?${queryString}` : ""}`, { credentials: "include", }).then((res) => res.json()), }); } export function useCreateProduct() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (newProduct: ProductCreate) => { return fetchWithAuth(`${Config.backend_uri}/products`, { method: "POST", credentials: "include", headers: { "Content-Type": "application/json", }, body: JSON.stringify(newProduct), }).then((res) => res.json()); }, onSuccess: async () => { notifications.show({ title: t("success", { capfirst: true }), message: t("success create", { capfirst: true, entity: t("product") }), }); await queryClient.invalidateQueries({ queryKey: ["products"] }); }, onError: (error) => { notifications.show({ title: t("error", { capfirst: true }), message: error?.message || t(`error create`, { capfirst: true, entity: t("of the productor") }), color: "red", }); }, }); } export function useDeleteProduct() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (id: number) => { return fetchWithAuth(`${Config.backend_uri}/products/${id}`, { method: "DELETE", credentials: "include", headers: { "Content-Type": "application/json", }, }).then((res) => res.json()); }, onSuccess: async () => { notifications.show({ title: t("success", { capfirst: true }), message: t("success delete", { capfirst: true, entity: t("product") }), }); await queryClient.invalidateQueries({ queryKey: ["products"] }); }, onError: (error) => { notifications.show({ title: t("error", { capfirst: true }), message: error?.message || t(`error delete`, { capfirst: true, entity: t("of the product") }), color: "red", }); }, }); } export function useEditProduct() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ id, product }: ProductEditPayload) => { return fetchWithAuth(`${Config.backend_uri}/products/${id}`, { method: "PUT", credentials: "include", headers: { "Content-Type": "application/json", }, body: JSON.stringify(product), }).then((res) => res.json()); }, onSuccess: async () => { notifications.show({ title: t("success", { capfirst: true }), message: t("success edit", { capfirst: true, entity: t("product") }), }); await queryClient.invalidateQueries({ queryKey: ["products"] }); }, onError: (error) => { notifications.show({ title: t("error", { capfirst: true }), message: error?.message || t(`error edit`, { capfirst: true, entity: t("of the product") }), color: "red", }); }, }); } export function useGetUser( id?: number, options?: Partial>, ) { return useQuery({ queryKey: ["user"], queryFn: () => fetchWithAuth(`${Config.backend_uri}/users/${id}`, { credentials: "include", }).then((res) => res.json()), enabled: !!id, ...options, }); } export function useGetUsers(filters?: URLSearchParams): UseQueryResult { const queryString = filters?.toString(); return useQuery({ queryKey: ["users", queryString], queryFn: () => fetchWithAuth(`${Config.backend_uri}/users${filters ? `?${queryString}` : ""}`, { credentials: "include", }).then((res) => res.json()), }); } export function useCreateUser() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (newUser: UserCreate) => { return fetchWithAuth(`${Config.backend_uri}/users`, { method: "POST", credentials: "include", headers: { "Content-Type": "application/json", }, body: JSON.stringify(newUser), }).then((res) => res.json()); }, onSuccess: async () => { notifications.show({ title: t("success", { capfirst: true }), message: t("success create", { capfirst: true, entity: t("user") }), }); await queryClient.invalidateQueries({ queryKey: ["users"] }); }, onError: (error) => { notifications.show({ title: t("error", { capfirst: true }), message: error?.message || t(`error create`, { capfirst: true, entity: t("of the user") }), color: "red", }); }, }); } export function useDeleteUser() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (id: number) => { return fetchWithAuth(`${Config.backend_uri}/users/${id}`, { method: "DELETE", credentials: "include", headers: { "Content-Type": "application/json", }, }).then((res) => res.json()); }, onSuccess: async () => { notifications.show({ title: t("success", { capfirst: true }), message: t("success delete", { capfirst: true, entity: t("user") }), }); await queryClient.invalidateQueries({ queryKey: ["users"] }); }, onError: (error) => { notifications.show({ title: t("error", { capfirst: true }), message: error?.message || t(`error delete`, { capfirst: true, entity: t("of the user") }), color: "red", }); }, }); } export function useEditUser() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ id, user }: UserEditPayload) => { return fetchWithAuth(`${Config.backend_uri}/users/${id}`, { method: "PUT", credentials: "include", headers: { "Content-Type": "application/json", }, body: JSON.stringify(user), }).then((res) => res.json()); }, onSuccess: async () => { notifications.show({ title: t("success", { capfirst: true }), message: t("success edit", { capfirst: true, entity: t("user") }), }); await queryClient.invalidateQueries({ queryKey: ["users"] }); }, onError: (error) => { notifications.show({ title: t("error", { capfirst: true }), message: error?.message || t(`error edit`, { capfirst: true, entity: t("of the user") }), color: "red", }); }, }); } export function useGetContracts(filters?: URLSearchParams): UseQueryResult { const queryString = filters?.toString(); return useQuery({ queryKey: ["contracts", queryString], queryFn: () => fetchWithAuth(`${Config.backend_uri}/contracts${filters ? `?${queryString}` : ""}`, { credentials: "include", }).then((res) => res.json()), }); } export function useGetContract( id?: number, options?: Partial>, ) { return useQuery({ queryKey: ["contract"], queryFn: () => fetchWithAuth(`${Config.backend_uri}/contracts/${id}`, { credentials: "include", }).then((res) => res.json()), enabled: !!id, ...options, }); } export function useGetContractFile() { return useMutation({ mutationFn: async (id: number) => { const res = await fetchWithAuth(`${Config.backend_uri}/contracts/${id}/file`, { credentials: "include", }).then((res) => res); if (!res.ok) throw new Error(); const blob = await res.blob(); const disposition = res.headers.get("Content-Disposition"); const filename = disposition && disposition?.includes("filename=") ? disposition.split("filename=")[1].replace(/"/g, "") : `contract_${id}.pdf`; return { blob, filename }; }, onSuccess: ({ blob, filename }) => { const url = URL.createObjectURL(blob); const link = document.createElement("a"); link.href = url; link.download = filename; link.click(); URL.revokeObjectURL(url); }, }); } export function useGetContractFileTemplate() { return useMutation({ mutationFn: async (form_id: number) => { const res = await fetchWithAuth(`${Config.backend_uri}/contracts/${form_id}/base`) .then((res) => res); if (!res.ok) throw new Error(); const blob = await res.blob(); const disposition = res.headers.get("Content-Disposition"); const filename = disposition && disposition?.includes("filename=") ? disposition.split("filename=")[1].replace(/"/g, "") : `contract_${form_id}.pdf`; return { blob, filename }; }, onSuccess: ({ blob, filename }) => { const url = URL.createObjectURL(blob); const link = document.createElement("a"); link.href = url; link.download = filename; link.click(); URL.revokeObjectURL(url); }, }); } export function useGetRecap() { return useMutation({ mutationFn: async (form_id: number) => { const res = await fetchWithAuth(`${Config.backend_uri}/contracts/${form_id}/recap`, { credentials: "include", }).then((res) => res); if (!res.ok) throw new Error(); const blob = await res.blob(); const disposition = res.headers.get("Content-Disposition"); const filename = disposition && disposition?.includes("filename=") ? disposition.split("filename=")[1].replace(/"/g, "") : `contract_recap_${form_id}.odt`; return { blob, filename }; }, onSuccess: ({ blob, filename }) => { const url = URL.createObjectURL(blob); const link = document.createElement("a"); link.href = url; link.download = filename; link.click(); URL.revokeObjectURL(url); }, }); } export function useGetAllContractFile() { return useMutation({ mutationFn: async (form_id: number) => { const res = await fetchWithAuth(`${Config.backend_uri}/contracts/${form_id}/files`, { credentials: "include", }).then((res) => res); if (!res.ok) throw new Error(); const blob = await res.blob(); const disposition = res.headers.get("Content-Disposition"); const filename = disposition && disposition?.includes("filename=") ? disposition.split("filename=")[1].replace(/"/g, "") : `contract_${form_id}.zip`; return { blob, filename }; }, onSuccess: ({ blob, filename }) => { const url = URL.createObjectURL(blob); const link = document.createElement("a"); link.href = url; link.download = filename; link.click(); URL.revokeObjectURL(url); }, }); } export function useCreateContract() { const queryClient = useQueryClient(); return useMutation({ mutationFn: async (newContract: ContractCreate) => { const res = await fetch(`${Config.backend_uri}/contracts`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(newContract), }).then((res) => res); if (!res.ok) throw new Error(); const blob = await res.blob(); const disposition = res.headers.get("Content-Disposition"); const filename = disposition && disposition?.includes("filename=") ? disposition.split("filename=")[1].replace(/"/g, "") : `contract_${newContract.form_id}.pdf`; return { blob, filename }; }, onSuccess: async ({ blob, filename }) => { const url = URL.createObjectURL(blob); const link = document.createElement("a"); link.href = url; link.download = filename; link.click(); URL.revokeObjectURL(url); await queryClient.invalidateQueries({ queryKey: ["contracts"] }); }, }); } export function useDeleteContract() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (id: number) => { return fetchWithAuth(`${Config.backend_uri}/contracts/${id}`, { method: "DELETE", credentials: "include", headers: { "Content-Type": "application/json", }, }).then((res) => res.json()); }, onSuccess: async () => { notifications.show({ title: t("success", { capfirst: true }), message: t("success delete", { capfirst: true, entity: t("contract") }), }); await queryClient.invalidateQueries({ queryKey: ["contracts"] }); }, onError: (error) => { notifications.show({ title: t("error", { capfirst: true }), message: error?.message || t(`error delete`, { capfirst: true, entity: t("of the contract") }), color: "red", }); }, }); } export function useGetRoles(filters?: URLSearchParams): UseQueryResult { const queryString = filters?.toString(); return useQuery({ queryKey: ["roles", queryString], queryFn: () => fetchWithAuth(`${Config.backend_uri}/users/roles${filters ? `?${queryString}` : ""}`, { credentials: "include", }).then((res) => res.json()), }); } export function useCurrentUser() { return useQuery({ queryKey: ["currentUser"], queryFn: () => { return fetchWithAuth(`${Config.backend_uri}/auth/user/me`, { credentials: "include", }, false).then((res) => res.json()); }, retry: false, }); }