This commit is contained in:
David Arranz 2026-04-05 14:01:52 +02:00
parent 9e1b975ea2
commit 56584d2bfd
19 changed files with 207 additions and 145 deletions

View File

@ -2,7 +2,7 @@ import type { CriteriaDTO } from "@erp/core";
import { useDebounce } from "@repo/rdx-ui/components";
import { useMemo, useState } from "react";
import { useCustomerListQuery } from "../../shared";
import { useCustomersListQuery } from "../../shared";
export const useListCustomersController = () => {
const [pageIndex, setPageIndex] = useState(0);
@ -21,7 +21,7 @@ export const useListCustomersController = () => {
};
}, [pageSize, pageIndex, debouncedQ]);
const query = useCustomerListQuery({ criteria });
const query = useCustomersListQuery({ criteria });
const setSearchValue = (value: string) => {
const nextValue = value.trim().replace(/\s+/g, " ");

View File

@ -1,6 +1,4 @@
import type { CreateCustomerResponseDTO } from "@erp/customers/common";
import type { GetCustomerByIdResult, UpdateCustomerByIdResult } from "../api";
import type { CreateCustomerResult, GetCustomerByIdResult, UpdateCustomerByIdResult } from "../api";
import type { Customer } from "../entities";
/**
@ -18,7 +16,7 @@ import type { Customer } from "../entities";
export const GetCustomerByIdAdapter = {
fromDTO(
dto: GetCustomerByIdResult | CreateCustomerResponseDTO | UpdateCustomerByIdResult,
dto: GetCustomerByIdResult | CreateCustomerResult | UpdateCustomerByIdResult,
context?: unknown
): Customer {
const taxesAdapter = (taxes: string) =>

View File

@ -24,13 +24,13 @@ export const useCustomerCreateMutation = () => {
return useMutation<Customer, DefaultError, CreateCustomerParams, CreateCustomerContext>({
mutationKey: CUSTOMER_CREATE_KEY,
mutationFn: async (payload) => {
const result = schema.safeParse(payload);
mutationFn: async (params) => {
const result = schema.safeParse(params);
if (!result.success) {
throw new ValidationErrorCollection("Validation failed", toValidationErrors(result.error));
}
const dto: CreateCustomerResult = await createCustomer(dataSource, payload);
const dto: CreateCustomerResult = await createCustomer(dataSource, params);
return GetCustomerByIdAdapter.fromDTO(dto);
},

View File

@ -8,25 +8,26 @@ import type { Customer } from "../entities";
import { CUSTOMER_QUERY_KEY } from "./keys";
type CustomerGetQueryOptions = {
id?: string;
enabled?: boolean;
};
export const useCustomerGetQuery = (
customerId?: string,
options?: CustomerGetQueryOptions
): UseQueryResult<Customer, DefaultError> => {
//const queryClient = useQueryClient();
const dataSource = useDataSource();
const enabled = options?.enabled ?? Boolean(customerId);
const id = options?.id;
const enabled = options?.enabled ?? Boolean(id);
return useQuery<Customer, DefaultError>({
queryKey: CUSTOMER_QUERY_KEY(customerId),
queryKey: CUSTOMER_QUERY_KEY(id),
queryFn: async ({ signal }) => {
const dto: GetCustomerByIdResult = await getCustomerById(
dataSource,
{ id: customerId! },
signal
);
if (!id) throw new Error("customerId is required");
const dto: GetCustomerByIdResult = await getCustomerById(dataSource, {
id: id!,
signal,
});
return GetCustomerByIdAdapter.fromDTO(dto);
},
enabled,

View File

@ -28,8 +28,8 @@ export const useCustomerUpdateMutation = () => {
return useMutation<Customer, DefaultError, UpdateCustomerByIdParams, UpdateCustomerContext>({
mutationKey: CUSTOMER_UPDATE_KEY,
mutationFn: async (payload) => {
const { id: customerId, data } = payload;
mutationFn: async (params) => {
const { id: customerId, data } = params;
if (!customerId) {
throw new Error("customerId is required");
}
@ -41,7 +41,7 @@ export const useCustomerUpdateMutation = () => {
const dto: UpdateCustomerByIdResult = await updateCustomerById(
dataSource,
payload as UpdateCustomerByIdParams
params as UpdateCustomerByIdParams
);
return GetCustomerByIdAdapter.fromDTO(dto);
},

View File

@ -8,13 +8,13 @@ import type { CustomerList } from "../entities";
import { LIST_CUSTOMERS_QUERY_KEY } from "./keys";
type CustomerListQueryOptions = {
enabled?: boolean;
type CustomersListQueryOptions = {
criteria?: Partial<CriteriaDTO>;
enabled?: boolean;
};
export const useCustomerListQuery = (
options?: CustomerListQueryOptions
export const useCustomersListQuery = (
options?: CustomersListQueryOptions
): UseQueryResult<CustomerList, DefaultError> => {
const dataSource = useDataSource();
const enabled = options?.enabled ?? true;
@ -23,11 +23,10 @@ export const useCustomerListQuery = (
return useQuery<CustomerList, DefaultError>({
queryKey: LIST_CUSTOMERS_QUERY_KEY(criteria),
queryFn: async ({ signal }) => {
const dto: ListCustomersResult = await getListCustomersByCriteria(
dataSource,
{ criteria },
signal
);
const dto: ListCustomersResult = await getListCustomersByCriteria(dataSource, {
criteria,
signal,
});
return ListCustomersAdapter.fromDTO(dto);
},
enabled,

View File

@ -4,4 +4,4 @@ import {
} from "./get-supplier-by-id.response.dto";
export const CreateSupplierResponseSchema = GetSupplierByIdResponseSchema;
export type SupplierCreationResponseDTO = GetSupplierByIdResponseDTO;
export type CreateSupplierResponseDTO = GetSupplierByIdResponseDTO;

View File

@ -1,8 +1,8 @@
import type { GetSupplierByIdResponseDTO } from "../../../common";
import type { CreateSupplierResult, GetSupplierByIdResult, UpdateSupplierByIdResult } from "../api";
import type { Supplier } from "../entities";
export const GetSupplierByIdAdapter = {
fromDTO(dto: GetSupplierByIdResponseDTO): Supplier {
fromDTO(dto: GetSupplierByIdResult | CreateSupplierResult | UpdateSupplierByIdResult): Supplier {
return {
id: dto.id,
companyId: dto.company_id,

View File

@ -1,24 +1,32 @@
import type { IDataSource } from "@erp/core/client";
import type { CreateSupplierRequestDTO, SupplierCreationResponseDTO } from "../../../common";
import type { CreateSupplierRequestDTO, CreateSupplierResponseDTO } from "../../../common";
export type SupplierCreateInput = CreateSupplierRequestDTO;
export type SupplierCreateOutput = SupplierCreationResponseDTO;
/**
* Crea un nuevo cliente en el sistema utilizando la fuente de datos proporcionada.
*
* @param dataSource - La fuente de datos para interactuar con la API.
* @param params - Los parámetros necesarios para crear el cliente.
* @returns Una promesa que resuelve con los detalles del cliente creado.
* @throws Error si el ID del cliente no es proporcionado o si la creación falla.
*/
export interface CreateSupplierParams {
id: string;
data: CreateSupplierRequestDTO;
}
export type CreateSupplierResult = CreateSupplierResponseDTO;
export function createSupplier(dataSource: IDataSource, params: CreateSupplierParams) {
const { id, data } = params;
export async function createSupplier(
dataSource: IDataSource,
id: string,
data: SupplierCreateInput
) {
if (!id) throw new Error("supplierId is required");
const response = await dataSource.createOne<SupplierCreateInput, SupplierCreateOutput>(
"suppliers",
{
...data,
id,
}
);
if (id !== data.id) throw new Error("supplierId in params must match id in data");
return response;
return dataSource.createOne<CreateSupplierRequestDTO, CreateSupplierResponseDTO>(
"suppliers",
data
);
}

View File

@ -1,13 +1,30 @@
import type { IDataSource } from "@erp/core/client";
import type { DeleteSupplierByIdRequestDTO } from "../../../common";
/**
* Elimina un proveedor existente en el sistema utilizando la fuente de datos proporcionada.
*
* @param dataSource - La fuente de datos para interactuar con la API.
* @param params - Los parámetros necesarios para eliminar el proveedor.
* @returns Una promesa que se resuelve cuando el proveedor ha sido eliminado exitosamente.
* @throws Error si el ID del proveedor no es proporcionado o si la eliminación falla.
*/
export type SupplierDeleteInput = DeleteSupplierByIdRequestDTO;
export interface DeleteSupplierByIdParams {
id: string;
signal?: AbortSignal;
}
export async function deleteSupplierById(dataSource: IDataSource, id: string, signal: AbortSignal) {
const response = await dataSource.deleteOne<SupplierDeleteInput>("suppliers", id, {
export type DeleteSupplierByIdResult = void;
export function deleteSupplierById(
dataSource: IDataSource,
params: DeleteSupplierByIdParams
): Promise<DeleteSupplierByIdResult> {
const { id, signal } = params;
if (!id) throw new Error("supplierId is required");
return dataSource.deleteOne<DeleteSupplierByIdResult>("suppliers", id, {
signal,
});
return response;
}

View File

@ -2,12 +2,27 @@ import type { IDataSource } from "@erp/core/client";
import type { GetSupplierByIdResponseDTO } from "../../../common";
export type SupplierGetOutput = GetSupplierByIdResponseDTO;
/**
* Recupera los detalles de un proveedor específico utilizando su ID a través de la fuente de datos proporcionada.
*
* @param dataSource - La fuente de datos para interactuar con la API.
* @param params - Los parámetros necesarios para obtener el proveedor, incluyendo su ID.
* @returns Una promesa que resuelve con los detalles del proveedor solicitado.
* @throws Error si el ID del proveedor no es proporcionado o si la recuperación falla.
*/
export async function getSupplierById(dataSource: IDataSource, id: string, signal: AbortSignal) {
const response = dataSource.getOne<SupplierGetOutput>("suppliers", id, {
signal,
});
return response;
export interface GetSupplierByIdParams {
id: string;
signal?: AbortSignal;
}
export type GetSupplierByIdResult = GetSupplierByIdResponseDTO;
export function getSupplierById(
dataSource: IDataSource,
params: GetSupplierByIdParams
): Promise<GetSupplierByIdResult> {
const { id, signal } = params;
if (!id) throw new Error("supplierId is required");
return dataSource.getOne<GetSupplierByIdResponseDTO>("suppliers", id, { signal });
}

View File

@ -3,17 +3,30 @@ import type { IDataSource } from "@erp/core/client";
import type { ListSuppliersResponseDTO } from "../../../common";
export type SupplierListOutput = ListSuppliersResponseDTO;
/**
* Recupera una lista de proveedores del sistema utilizando la
* fuente de datos proporcionada y los criterios de búsqueda especificados.
*
* @param dataSource - La fuente de datos para interactuar con la API.
* @param params - Los parámetros necesarios para listar los proveedores, incluyendo los criterios de búsqueda.
* @returns Una promesa que resuelve con una lista de proveedores que cumplen con los criterios especificados.
* @throws Error si la recuperación de la lista de proveedores falla.
*/
export async function getListSuppliers(
export type ListSuppliersByCriteriaParams = {
criteria?: CriteriaDTO;
signal?: AbortSignal;
};
export type ListSuppliersResult = ListSuppliersResponseDTO;
export function getListSuppliersByCriteria(
dataSource: IDataSource,
criteria: CriteriaDTO,
signal: AbortSignal
) {
const response = dataSource.getList<SupplierListOutput>("suppliers", {
params: ListSuppliersByCriteriaParams
): Promise<ListSuppliersResult> {
const { criteria, signal } = params || { criteria: undefined, signal: undefined };
return dataSource.getList<ListSuppliersResponseDTO>("suppliers", {
signal,
...criteria,
});
return response;
}

View File

@ -2,21 +2,32 @@ import type { IDataSource } from "@erp/core/client";
import type { UpdateSupplierByIdRequestDTO, UpdateSupplierByIdResponseDTO } from "../../../common";
export type SupplierUpdateInput = UpdateSupplierByIdRequestDTO;
export type SupplierUpdateOutput = UpdateSupplierByIdResponseDTO;
/**
* Actualiza un proveedor existente en el sistema utilizando la fuente de datos proporcionada.
*
* @param dataSource - La fuente de datos para interactuar con la API.
* @param params - Los parámetros necesarios para actualizar el proveedor.
* @returns Una promesa que resuelve con los detalles del proveedor actualizado.
* @throws Error si el ID del proveedor no es proporcionado o si la actualización falla.
*/
export async function updateSupplierById(
export type UpdateSupplierByIdParams = {
id: string;
data: UpdateSupplierByIdRequestDTO;
};
export type UpdateSupplierByIdResult = UpdateSupplierByIdResponseDTO;
export function updateSupplierById(
dataSource: IDataSource,
id: string,
data: SupplierUpdateInput
) {
if (!id) throw new Error("supplierId is required");
params: UpdateSupplierByIdParams
): Promise<UpdateSupplierByIdResult> {
const { id, data } = params;
const response = dataSource.updateOne<SupplierUpdateInput, SupplierUpdateOutput>(
if (!id) throw new Error("supplierId is required");
return dataSource.updateOne<UpdateSupplierByIdRequestDTO, UpdateSupplierByIdResponseDTO>(
"suppliers",
id,
data
);
return response;
}

View File

@ -1,8 +1,8 @@
export * from "./keys";
export * from "./supplier-cache-strategy";
export * from "./to-validation-errors";
export * from "./use-list-suppliers-query";
export * from "./use-supplier-create-mutation";
export * from "./use-supplier-delete-mutation";
export * from "./use-supplier-get-query";
export * from "./use-supplier-update-mutation";
export * from "./use-suppliers-list-query";

View File

@ -1,10 +1,10 @@
import { useDataSource } from "@erp/core/hooks";
import { UniqueID, ValidationErrorCollection } from "@repo/rdx-ddd";
import { ValidationErrorCollection } from "@repo/rdx-ddd";
import { type DefaultError, useMutation, useQueryClient } from "@tanstack/react-query";
import { CreateSupplierRequestSchema } from "../../../common";
import { GetSupplierByIdAdapter } from "../adapters";
import { type SupplierCreateInput, createSupplier } from "../api";
import { type CreateSupplierParams, type CreateSupplierResult, createSupplier } from "../api";
import type { Supplier } from "../entities";
import { SUPPLIER_CREATE_KEY } from "./keys";
@ -16,27 +16,21 @@ import { toValidationErrors } from "./to-validation-errors";
type CreateSupplierContext = {};
type CreateSupplierPayload = {
data: SupplierCreateInput;
};
export const useSupplierCreateMutation = () => {
const queryClient = useQueryClient();
const dataSource = useDataSource();
const schema = CreateSupplierRequestSchema;
return useMutation<Supplier, DefaultError, CreateSupplierPayload, CreateSupplierContext>({
return useMutation<Supplier, DefaultError, CreateSupplierParams, CreateSupplierContext>({
mutationKey: SUPPLIER_CREATE_KEY,
mutationFn: async ({ data }) => {
const id = UniqueID.generateNewID().toString();
const result = schema.safeParse(data);
mutationFn: async (params) => {
const result = schema.safeParse(params);
if (!result.success) {
throw new ValidationErrorCollection("Validation failed", toValidationErrors(result.error));
}
const dto = await createSupplier(dataSource, id, data);
const dto: CreateSupplierResult = await createSupplier(dataSource, params);
return GetSupplierByIdAdapter.fromDTO(dto);
},

View File

@ -1,7 +1,7 @@
import { useDataSource } from "@erp/core/hooks";
import { type DefaultError, useMutation, useQueryClient } from "@tanstack/react-query";
import { deleteSupplierById } from "../api";
import { type DeleteSupplierByIdParams, deleteSupplierById } from "../api";
import { SUPPLIER_DELETE_KEY } from "./keys";
import {
@ -12,42 +12,39 @@ import {
rollbackDeleteSupplierOptimisticUpdate,
} from "./supplier-cache-strategy";
export interface DeleteSupplierPayload {
id: string;
}
interface DeleteSupplierContext extends DeleteSupplierCacheContext {}
export const useSupplierDeleteMutation = () => {
const queryClient = useQueryClient();
const dataSource = useDataSource();
return useMutation<{ id: string }, DefaultError, DeleteSupplierPayload, DeleteSupplierContext>({
mutationKey: SUPPLIER_DELETE_KEY,
return useMutation<{ id: string }, DefaultError, DeleteSupplierByIdParams, DeleteSupplierContext>(
{
mutationKey: SUPPLIER_DELETE_KEY,
mutationFn: async ({ id, signal }) => {
if (!id) {
throw new Error("customerId is required");
}
mutationFn: async ({ id }) => {
if (!id) {
throw new Error("supplierId is required");
}
await deleteSupplierById(dataSource, { id, signal });
return { id };
},
await deleteSupplierById(dataSource, id, new AbortController().signal);
return { id };
},
onMutate: async ({ id }) => {
return prepareDeleteSupplierOptimisticUpdate(queryClient, id);
},
onMutate: async ({ id }) => {
return prepareDeleteSupplierOptimisticUpdate(queryClient, id);
},
onError: (_error, _variables, context) => {
rollbackDeleteSupplierOptimisticUpdate(queryClient, context);
},
onError: (_error, _variables, context) => {
rollbackDeleteSupplierOptimisticUpdate(queryClient, context);
},
onSuccess: ({ id }) => {
finalizeDeletedSupplierCaches(queryClient, id);
},
onSuccess: ({ id }) => {
finalizeDeletedSupplierCaches(queryClient, id);
},
onSettled: async () => {
await invalidateSupplierListQueries(queryClient);
},
});
onSettled: async () => {
await invalidateSupplierListQueries(queryClient);
},
}
);
};

View File

@ -1,4 +1,3 @@
import { useDataSource } from "@erp/core/hooks";
import { type DefaultError, type UseQueryResult, useQuery } from "@tanstack/react-query";
import { GetSupplierByIdAdapter } from "../adapters";
@ -8,20 +7,25 @@ import type { Supplier } from "../entities";
import { SUPPLIER_QUERY_KEY } from "./keys";
type SupplierGetQueryOptions = {
id?: string;
enabled?: boolean;
};
export const useSupplierGetQuery = (
supplierId?: string,
options?: SupplierGetQueryOptions
): UseQueryResult<Supplier, DefaultError> => {
const dataSource = useDataSource();
const enabled = options?.enabled ?? Boolean(supplierId);
const id = options?.id;
const enabled = options?.enabled ?? Boolean(id);
return useQuery<Supplier, DefaultError>({
queryKey: SUPPLIER_QUERY_KEY(supplierId),
queryKey: SUPPLIER_QUERY_KEY(id),
queryFn: async ({ signal }) => {
const dto = await getSupplierById(dataSource, String(supplierId), signal);
if (!id) throw new Error("supplierId is required");
const dto: GetSupplierByIdResult = await getSupplierById(dataSource, {
id: id!,
signal,
});
return GetSupplierByIdAdapter.fromDTO(dto);
},
enabled,

View File

@ -4,7 +4,11 @@ import { type DefaultError, useMutation, useQueryClient } from "@tanstack/react-
import { UpdateSupplierByIdRequestSchema } from "../../../common";
import { GetSupplierByIdAdapter } from "../adapters";
import { type SupplierUpdateInput, updateSupplierById } from "../api";
import {
type UpdateSupplierByIdParams,
type UpdateSupplierByIdResult,
updateSupplierById,
} from "../api";
import type { Supplier } from "../entities";
import { SUPPLIER_UPDATE_KEY } from "./keys";
@ -16,21 +20,17 @@ import { toValidationErrors } from "./to-validation-errors";
type UpdateSupplierContext = {};
type UpdateSupplierPayload = {
id: string;
data: SupplierUpdateInput;
};
export const useSupplierUpdateMutation = () => {
const queryClient = useQueryClient();
const dataSource = useDataSource();
const schema = UpdateSupplierByIdRequestSchema;
return useMutation<Supplier, DefaultError, UpdateSupplierPayload, UpdateSupplierContext>({
return useMutation<Supplier, DefaultError, UpdateSupplierByIdParams, UpdateSupplierContext>({
mutationKey: SUPPLIER_UPDATE_KEY,
mutationFn: async ({ id, data }) => {
if (!id) {
mutationFn: async (params) => {
const { id: supplierId, data } = params;
if (!supplierId) {
throw new Error("supplierId is required");
}
@ -39,7 +39,10 @@ export const useSupplierUpdateMutation = () => {
throw new ValidationErrorCollection("Validation failed", toValidationErrors(result.error));
}
const dto = await updateSupplierById(dataSource, id, data);
const dto: UpdateSupplierByIdResult = await updateSupplierById(
dataSource,
params as UpdateSupplierByIdParams
);
return GetSupplierByIdAdapter.fromDTO(dto);
},

View File

@ -3,30 +3,32 @@ import { useDataSource } from "@erp/core/hooks";
import { type DefaultError, type UseQueryResult, useQuery } from "@tanstack/react-query";
import { ListSuppliersAdapter } from "../adapters";
import { getListSuppliers } from "../api";
import type { SupplierList } from "../entities";
import { LIST_SUPPLIERS_QUERY_KEY } from "./keys";
type ListSuppliersQueryOptions = {
enabled?: boolean;
type SuppliersListQueryOptions = {
criteria?: Partial<CriteriaDTO>;
enabled?: boolean;
};
export const useListSuppliersQuery = (
options?: ListSuppliersQueryOptions
export const useSuppliersListQuery = (
options?: SuppliersListQueryOptions
): UseQueryResult<SupplierList, DefaultError> => {
const dataSource = useDataSource();
const enabled = options?.enabled ?? true;
const criteria = options?.criteria ?? {};
return useQuery<SupplierList, DefaultError>({
queryKey: LIST_SUPPLIERS_QUERY_KEY(criteria as CriteriaDTO),
queryKey: LIST_SUPPLIERS_QUERY_KEY(criteria),
queryFn: async ({ signal }) => {
const dto = await getListSuppliers(dataSource, criteria as CriteriaDTO, signal);
const dto: ListSuppliersResult = await getListSuppliersByCriteria(dataSource, {
criteria,
signal,
});
return ListSuppliersAdapter.fromDTO(dto);
},
enabled,
placeholderData: (previousData) => previousData,
placeholderData: (previousData) => previousData, // Mantiene la página anterior durante refetch por cambio de criteria
});
};