/** * useContracts Hook - Contratos, Subcontratistas, Partidas, Addendas */ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { AxiosError } from 'axios'; import toast from 'react-hot-toast'; import type { ApiError } from '../services/api'; import { contractsApi, subcontractorsApi, contractPartidasApi, contractAddendumsApi, } from '../services/contracts'; import type { ContractFilters, CreateContractDto, UpdateContractDto, SubcontractorFilters, CreateSubcontractorDto, UpdateSubcontractorDto, CreateContractPartidaDto, UpdateContractPartidaDto, CreateAddendumDto, UpdateAddendumDto, } from '../types/contracts.types'; // ============================================================================ // QUERY KEYS // ============================================================================ export const contractsKeys = { // Contracts contracts: { all: ['contracts'] as const, list: (filters?: ContractFilters) => [...contractsKeys.contracts.all, 'list', filters] as const, detail: (id: string) => [...contractsKeys.contracts.all, 'detail', id] as const, stats: () => [...contractsKeys.contracts.all, 'stats'] as const, }, // Subcontractors subcontractors: { all: ['subcontractors'] as const, list: (filters?: SubcontractorFilters) => [...contractsKeys.subcontractors.all, 'list', filters] as const, detail: (id: string) => [...contractsKeys.subcontractors.all, 'detail', id] as const, }, // Contract Partidas partidas: { all: ['contract-partidas'] as const, list: (contractId: string) => [...contractsKeys.partidas.all, 'list', contractId] as const, }, // Contract Addendums addendums: { all: ['contract-addendums'] as const, list: (contractId: string) => [...contractsKeys.addendums.all, 'list', contractId] as const, detail: (contractId: string, addendumId: string) => [...contractsKeys.addendums.all, 'detail', contractId, addendumId] as const, }, }; // ============================================================================ // ERROR HANDLER // ============================================================================ const handleError = (error: AxiosError) => { const message = error.response?.data?.message || 'Ha ocurrido un error'; toast.error(message); }; // ============================================================================ // CONTRACTS HOOKS // ============================================================================ export function useContracts(filters?: ContractFilters) { return useQuery({ queryKey: contractsKeys.contracts.list(filters), queryFn: () => contractsApi.list(filters), }); } export function useContract(id: string) { return useQuery({ queryKey: contractsKeys.contracts.detail(id), queryFn: () => contractsApi.get(id), enabled: !!id, }); } export function useContractStats() { return useQuery({ queryKey: contractsKeys.contracts.stats(), queryFn: () => contractsApi.stats(), }); } export function useCreateContract() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (data: CreateContractDto) => contractsApi.create(data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: contractsKeys.contracts.all }); toast.success('Contrato creado exitosamente'); }, onError: handleError, }); } export function useUpdateContract() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ id, data }: { id: string; data: UpdateContractDto }) => contractsApi.update(id, data), onSuccess: (_, { id }) => { queryClient.invalidateQueries({ queryKey: contractsKeys.contracts.all }); queryClient.invalidateQueries({ queryKey: contractsKeys.contracts.detail(id) }); toast.success('Contrato actualizado'); }, onError: handleError, }); } export function useDeleteContract() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (id: string) => contractsApi.delete(id), onSuccess: () => { queryClient.invalidateQueries({ queryKey: contractsKeys.contracts.all }); toast.success('Contrato eliminado'); }, onError: handleError, }); } export function useSubmitContract() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (id: string) => contractsApi.submit(id), onSuccess: (_, id) => { queryClient.invalidateQueries({ queryKey: contractsKeys.contracts.all }); queryClient.invalidateQueries({ queryKey: contractsKeys.contracts.detail(id) }); toast.success('Contrato enviado a revision'); }, onError: handleError, }); } export function useApproveContract() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (id: string) => contractsApi.approve(id), onSuccess: (_, id) => { queryClient.invalidateQueries({ queryKey: contractsKeys.contracts.all }); queryClient.invalidateQueries({ queryKey: contractsKeys.contracts.detail(id) }); toast.success('Contrato aprobado'); }, onError: handleError, }); } export function useActivateContract() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (id: string) => contractsApi.activate(id), onSuccess: (_, id) => { queryClient.invalidateQueries({ queryKey: contractsKeys.contracts.all }); queryClient.invalidateQueries({ queryKey: contractsKeys.contracts.detail(id) }); toast.success('Contrato activado'); }, onError: handleError, }); } export function useCompleteContract() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (id: string) => contractsApi.complete(id), onSuccess: (_, id) => { queryClient.invalidateQueries({ queryKey: contractsKeys.contracts.all }); queryClient.invalidateQueries({ queryKey: contractsKeys.contracts.detail(id) }); toast.success('Contrato completado'); }, onError: handleError, }); } export function useTerminateContract() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ id, reason }: { id: string; reason: string }) => contractsApi.terminate(id, reason), onSuccess: (_, { id }) => { queryClient.invalidateQueries({ queryKey: contractsKeys.contracts.all }); queryClient.invalidateQueries({ queryKey: contractsKeys.contracts.detail(id) }); toast.success('Contrato terminado'); }, onError: handleError, }); } // ============================================================================ // SUBCONTRACTORS HOOKS // ============================================================================ export function useSubcontractors(filters?: SubcontractorFilters) { return useQuery({ queryKey: contractsKeys.subcontractors.list(filters), queryFn: () => subcontractorsApi.list(filters), }); } export function useSubcontractor(id: string) { return useQuery({ queryKey: contractsKeys.subcontractors.detail(id), queryFn: () => subcontractorsApi.get(id), enabled: !!id, }); } export function useCreateSubcontractor() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (data: CreateSubcontractorDto) => subcontractorsApi.create(data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: contractsKeys.subcontractors.all }); toast.success('Subcontratista creado exitosamente'); }, onError: handleError, }); } export function useUpdateSubcontractor() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ id, data }: { id: string; data: UpdateSubcontractorDto }) => subcontractorsApi.update(id, data), onSuccess: (_, { id }) => { queryClient.invalidateQueries({ queryKey: contractsKeys.subcontractors.all }); queryClient.invalidateQueries({ queryKey: contractsKeys.subcontractors.detail(id) }); toast.success('Subcontratista actualizado'); }, onError: handleError, }); } export function useDeleteSubcontractor() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (id: string) => subcontractorsApi.delete(id), onSuccess: () => { queryClient.invalidateQueries({ queryKey: contractsKeys.subcontractors.all }); toast.success('Subcontratista eliminado'); }, onError: handleError, }); } export function useActivateSubcontractor() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (id: string) => subcontractorsApi.activate(id), onSuccess: (_, id) => { queryClient.invalidateQueries({ queryKey: contractsKeys.subcontractors.all }); queryClient.invalidateQueries({ queryKey: contractsKeys.subcontractors.detail(id) }); toast.success('Subcontratista activado'); }, onError: handleError, }); } export function useDeactivateSubcontractor() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (id: string) => subcontractorsApi.deactivate(id), onSuccess: (_, id) => { queryClient.invalidateQueries({ queryKey: contractsKeys.subcontractors.all }); queryClient.invalidateQueries({ queryKey: contractsKeys.subcontractors.detail(id) }); toast.success('Subcontratista desactivado'); }, onError: handleError, }); } export function useBlacklistSubcontractor() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ id, reason }: { id: string; reason: string }) => subcontractorsApi.blacklist(id, reason), onSuccess: (_, { id }) => { queryClient.invalidateQueries({ queryKey: contractsKeys.subcontractors.all }); queryClient.invalidateQueries({ queryKey: contractsKeys.subcontractors.detail(id) }); toast.success('Subcontratista agregado a lista negra'); }, onError: handleError, }); } // ============================================================================ // CONTRACT PARTIDAS HOOKS // ============================================================================ export function useContractPartidas(contractId: string) { return useQuery({ queryKey: contractsKeys.partidas.list(contractId), queryFn: () => contractPartidasApi.list(contractId), enabled: !!contractId, }); } export function useCreateContractPartida() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ contractId, data }: { contractId: string; data: CreateContractPartidaDto }) => contractPartidasApi.create(contractId, data), onSuccess: (_, { contractId }) => { queryClient.invalidateQueries({ queryKey: contractsKeys.partidas.list(contractId) }); queryClient.invalidateQueries({ queryKey: contractsKeys.contracts.detail(contractId) }); toast.success('Partida agregada'); }, onError: handleError, }); } export function useUpdateContractPartida() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ contractId, partidaId, data }: { contractId: string; partidaId: string; data: UpdateContractPartidaDto }) => contractPartidasApi.update(contractId, partidaId, data), onSuccess: (_, { contractId }) => { queryClient.invalidateQueries({ queryKey: contractsKeys.partidas.list(contractId) }); queryClient.invalidateQueries({ queryKey: contractsKeys.contracts.detail(contractId) }); toast.success('Partida actualizada'); }, onError: handleError, }); } export function useDeleteContractPartida() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ contractId, partidaId }: { contractId: string; partidaId: string }) => contractPartidasApi.delete(contractId, partidaId), onSuccess: (_, { contractId }) => { queryClient.invalidateQueries({ queryKey: contractsKeys.partidas.list(contractId) }); queryClient.invalidateQueries({ queryKey: contractsKeys.contracts.detail(contractId) }); toast.success('Partida eliminada'); }, onError: handleError, }); } // ============================================================================ // CONTRACT ADDENDUMS HOOKS // ============================================================================ export function useContractAddendums(contractId: string) { return useQuery({ queryKey: contractsKeys.addendums.list(contractId), queryFn: () => contractAddendumsApi.list(contractId), enabled: !!contractId, }); } export function useContractAddendum(contractId: string, addendumId: string) { return useQuery({ queryKey: contractsKeys.addendums.detail(contractId, addendumId), queryFn: () => contractAddendumsApi.get(contractId, addendumId), enabled: !!contractId && !!addendumId, }); } export function useCreateContractAddendum() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ contractId, data }: { contractId: string; data: CreateAddendumDto }) => contractAddendumsApi.create(contractId, data), onSuccess: (_, { contractId }) => { queryClient.invalidateQueries({ queryKey: contractsKeys.addendums.list(contractId) }); queryClient.invalidateQueries({ queryKey: contractsKeys.contracts.detail(contractId) }); toast.success('Addenda creada'); }, onError: handleError, }); } export function useUpdateContractAddendum() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ contractId, addendumId, data }: { contractId: string; addendumId: string; data: UpdateAddendumDto }) => contractAddendumsApi.update(contractId, addendumId, data), onSuccess: (_, { contractId, addendumId }) => { queryClient.invalidateQueries({ queryKey: contractsKeys.addendums.list(contractId) }); queryClient.invalidateQueries({ queryKey: contractsKeys.addendums.detail(contractId, addendumId) }); toast.success('Addenda actualizada'); }, onError: handleError, }); } export function useDeleteContractAddendum() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ contractId, addendumId }: { contractId: string; addendumId: string }) => contractAddendumsApi.delete(contractId, addendumId), onSuccess: (_, { contractId }) => { queryClient.invalidateQueries({ queryKey: contractsKeys.addendums.list(contractId) }); toast.success('Addenda eliminada'); }, onError: handleError, }); } export function useSubmitAddendum() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ contractId, addendumId }: { contractId: string; addendumId: string }) => contractAddendumsApi.submit(contractId, addendumId), onSuccess: (_, { contractId, addendumId }) => { queryClient.invalidateQueries({ queryKey: contractsKeys.addendums.list(contractId) }); queryClient.invalidateQueries({ queryKey: contractsKeys.addendums.detail(contractId, addendumId) }); toast.success('Addenda enviada a revision'); }, onError: handleError, }); } export function useApproveAddendum() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ contractId, addendumId }: { contractId: string; addendumId: string }) => contractAddendumsApi.approve(contractId, addendumId), onSuccess: (_, { contractId, addendumId }) => { queryClient.invalidateQueries({ queryKey: contractsKeys.addendums.list(contractId) }); queryClient.invalidateQueries({ queryKey: contractsKeys.addendums.detail(contractId, addendumId) }); queryClient.invalidateQueries({ queryKey: contractsKeys.contracts.detail(contractId) }); toast.success('Addenda aprobada'); }, onError: handleError, }); } export function useRejectAddendum() { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ contractId, addendumId, reason }: { contractId: string; addendumId: string; reason: string }) => contractAddendumsApi.reject(contractId, addendumId, reason), onSuccess: (_, { contractId, addendumId }) => { queryClient.invalidateQueries({ queryKey: contractsKeys.addendums.list(contractId) }); queryClient.invalidateQueries({ queryKey: contractsKeys.addendums.detail(contractId, addendumId) }); toast.success('Addenda rechazada'); }, onError: handleError, }); }